@tsparticles/all
Version:
All-inclusive tsParticles bundle — all engine packages, plugins, interactions, presets, shapes, updaters, effects, paths, emitters, sounds, and palettes in one dependency. Ready to use components available for React, Vue.js (2.x and 3.x), Angular, Svelte,
28,656 lines • 1.11 MB
JavaScript
(function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
/* tsParticles v4.4.0 */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.bundles = global.__tsParticlesInternals.bundles || {}, global.__tsParticlesInternals.bundles.all = global.__tsParticlesInternals.bundles.all || {})));
})(this, (function (exports) { 'use strict';
const generatedAttribute = "generated", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half$2 = 0.5, millisecondsToSeconds$1 = 1000, originPoint = {
x: 0,
y: 0,
z: 0,
}, defaultTransform$1 = {
a: 1,
b: 0,
c: 0,
d: 1,
}, randomColorValue = "random", midColorValue = "mid", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha$1 = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio$1 = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty$1 = 0, quarter$2 = 0.25, threeQuarter = half$2 + quarter$2, minVelocity = 0, minDistance$7 = 0, minRadius$8 = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$3 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex$1 = 0, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, defaultAngle$1 = 0, identity$3 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
exports.MoveDirection = void 0;
(function (MoveDirection) {
MoveDirection["bottom"] = "bottom";
MoveDirection["bottomLeft"] = "bottom-left";
MoveDirection["bottomRight"] = "bottom-right";
MoveDirection["left"] = "left";
MoveDirection["none"] = "none";
MoveDirection["right"] = "right";
MoveDirection["top"] = "top";
MoveDirection["topLeft"] = "top-left";
MoveDirection["topRight"] = "top-right";
MoveDirection["outside"] = "outside";
MoveDirection["inside"] = "inside";
})(exports.MoveDirection || (exports.MoveDirection = {}));
function getZ(source) {
return "z" in source ? source.z : originPoint.z;
}
class Vector3d {
x;
y;
z;
constructor(x = originPoint.x, y = originPoint.y, z = originPoint.z) {
this.x = x;
this.y = y;
this.z = z;
}
static get origin() {
return Vector3d.create(originPoint.x, originPoint.y, originPoint.z);
}
get angle() {
return Math.atan2(this.y, this.x);
}
set angle(angle) {
this.#updateFromAngle(angle, this.length);
}
get length() {
return Math.sqrt(this.getLengthSq());
}
set length(length) {
this.#updateFromAngle(this.angle, length);
}
static clone(source) {
return Vector3d.create(source.x, source.y, getZ(source));
}
static create(x, y, z) {
if (typeof x === "number") {
return new Vector3d(x, y ?? originPoint.y, z ?? originPoint.z);
}
return new Vector3d(x.x, x.y, getZ(x));
}
add(v) {
return Vector3d.create(this.x + v.x, this.y + v.y, this.z + getZ(v));
}
addTo(v) {
this.x += v.x;
this.y += v.y;
this.z += getZ(v);
}
copy() {
return Vector3d.clone(this);
}
div(n) {
return Vector3d.create(this.x / n, this.y / n, this.z / n);
}
getLengthSq() {
return this.x ** squareExp + this.y ** squareExp;
}
mult(n) {
return Vector3d.create(this.x * n, this.y * n, this.z * n);
}
multTo(n) {
this.x *= n;
this.y *= n;
this.z *= n;
}
normalize() {
const length = this.length;
if (length != none) {
this.multTo(inverseFactorNumerator / length);
}
}
rotate(angle) {
return Vector3d.create(this.x * Math.cos(angle) - this.y * Math.sin(angle), this.x * Math.sin(angle) + this.y * Math.cos(angle), originPoint.z);
}
setTo(c) {
this.x = c.x;
this.y = c.y;
this.z = getZ(c);
}
sub(v) {
return Vector3d.create(this.x - v.x, this.y - v.y, this.z - getZ(v));
}
subFrom(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= getZ(v);
}
#updateFromAngle(angle, length) {
this.x = Math.cos(angle) * length;
this.y = Math.sin(angle) * length;
}
}
class Vector extends Vector3d {
constructor(x = originPoint.x, y = originPoint.y) {
super(x, y, originPoint.z);
}
static get origin() {
return Vector.create(originPoint.x, originPoint.y);
}
static clone(source) {
return Vector.create(source.x, source.y);
}
static create(x, y) {
if (typeof x === "number") {
return new Vector(x, y ?? originPoint.y);
}
return new Vector(x.x, x.y);
}
}
function isBoolean(arg) {
return typeof arg === "boolean";
}
function isString(arg) {
return typeof arg === "string";
}
function isNumber(arg) {
return typeof arg === "number";
}
function isFunction(arg) {
return typeof arg === "function";
}
function isObject(arg) {
return typeof arg === "object" && arg !== null;
}
function isArray(arg) {
return Array.isArray(arg);
}
function isNull(arg) {
return arg === null || arg === undefined;
}
const piDeg$1 = 180, degToRadFactor = Math.PI / piDeg$1;
let _random = Math.random;
const _animationLoop = {
nextFrame: (cb) => requestAnimationFrame(cb),
cancel: (idx) => {
cancelAnimationFrame(idx);
},
};
function setRandom(rnd = Math.random) {
_random = rnd;
}
function getRandomFn() {
return _random;
}
function getRandom() {
const min = 0, max = 1;
return clamp(_random(), min, max - Number.EPSILON);
}
function getRandomInRange(min, max) {
return getRandom() * (max - min) + min;
}
function setAnimationFunctions(nextFrame, cancel) {
_animationLoop.nextFrame = nextFrame;
_animationLoop.cancel = cancel;
}
function animate(fn) {
return _animationLoop.nextFrame(fn);
}
function cancelAnimation(handle) {
_animationLoop.cancel(handle);
}
function clamp(num, min, max) {
return Math.min(Math.max(num, min), max);
}
function mix(comp1, comp2, weight1, weight2) {
return Math.floor((comp1 * weight1 + comp2 * weight2) / (weight1 + weight2));
}
function randomInRangeValue(r) {
const max = getRangeMax(r), minOffset = 0;
let min = getRangeMin(r);
if (max === min) {
min = minOffset;
}
return getRandomInRange(min, max);
}
function getRangeValue(value) {
return isNumber(value) ? value : randomInRangeValue(value);
}
function getRangeMin(value) {
return isNumber(value) ? value : value.min;
}
function getRangeMax(value) {
return isNumber(value) ? value : value.max;
}
function setRangeValue(source, value) {
if (source === value || (value === undefined && isNumber(source))) {
return source;
}
const min = getRangeMin(source), max = getRangeMax(source);
return value !== undefined
? {
min: Math.min(min, value),
max: Math.max(max, value),
}
: setRangeValue(min, max);
}
function getDistances(pointA, pointB) {
const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y;
return { dx: dx, dy: dy, distance: Math.hypot(dx, dy) };
}
function getDistanceSq(pointA, pointB) {
const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y;
return dx * dx + dy * dy;
}
function getDistance(pointA, pointB) {
return Math.sqrt(getDistanceSq(pointA, pointB));
}
function checkDistance(pointA, pointB, distance) {
return getDistanceSq(pointA, pointB) <= distance * distance;
}
function degToRad(degrees) {
return degrees * degToRadFactor;
}
function getParticleDirectionAngle(direction, position, center) {
if (isNumber(direction)) {
return degToRad(direction);
}
switch (direction) {
case exports.MoveDirection.top:
return -Math.PI * half$2;
case exports.MoveDirection.topRight:
return -Math.PI * quarter$2;
case exports.MoveDirection.right:
return empty$1;
case exports.MoveDirection.bottomRight:
return Math.PI * quarter$2;
case exports.MoveDirection.bottom:
return Math.PI * half$2;
case exports.MoveDirection.bottomLeft:
return Math.PI * threeQuarter;
case exports.MoveDirection.left:
return Math.PI;
case exports.MoveDirection.topLeft:
return -Math.PI * threeQuarter;
case exports.MoveDirection.inside:
return Math.atan2(center.y - position.y, center.x - position.x);
case exports.MoveDirection.outside:
return Math.atan2(position.y - center.y, position.x - center.x);
default:
return getRandom() * doublePI;
}
}
function getParticleBaseVelocity(direction) {
const baseVelocity = Vector.origin;
baseVelocity.length = 1;
baseVelocity.angle = direction;
return baseVelocity;
}
function collisionVelocity(v1, v2, m1, m2) {
return Vector.create((v1.x * (m1 - m2)) / (m1 + m2) + (v2.x * double * m2) / (m1 + m2), v1.y);
}
function calcPositionFromSize(data) {
return data.position?.x !== undefined && data.position.y !== undefined
? {
x: (data.position.x * data.size.width) / percentDenominator,
y: (data.position.y * data.size.height) / percentDenominator,
}
: undefined;
}
function calcPositionOrRandomFromSize(data) {
return {
x: ((data.position?.x ?? getRandom() * percentDenominator) * data.size.width) / percentDenominator,
y: ((data.position?.y ?? getRandom() * percentDenominator) * data.size.height) / percentDenominator,
};
}
function calcPositionOrRandomFromSizeRanged(data) {
const position = {
x: data.position?.x !== undefined ? getRangeValue(data.position.x) : undefined,
y: data.position?.y !== undefined ? getRangeValue(data.position.y) : undefined,
};
return calcPositionOrRandomFromSize({ size: data.size, position });
}
function calcExactPositionOrRandomFromSize(data) {
const { position, size } = data;
return {
x: position?.x ?? getRandom() * size.width,
y: position?.y ?? getRandom() * size.height,
};
}
function parseAlpha(input) {
const defaultAlpha = 1;
if (!input) {
return defaultAlpha;
}
return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
}
exports.OutModeDirection = void 0;
(function (OutModeDirection) {
OutModeDirection["bottom"] = "bottom";
OutModeDirection["left"] = "left";
OutModeDirection["right"] = "right";
OutModeDirection["top"] = "top";
})(exports.OutModeDirection || (exports.OutModeDirection = {}));
exports.PixelMode = void 0;
(function (PixelMode) {
PixelMode["precise"] = "precise";
PixelMode["percent"] = "percent";
})(exports.PixelMode || (exports.PixelMode = {}));
const minRadius$7 = 0;
function isForbiddenKey(key) {
return key === "__proto__" || key === "constructor" || key === "prototype";
}
function hasMatchMedia() {
return typeof matchMedia !== "undefined";
}
function safeDocument() {
return globalThis.document;
}
function safeMatchMedia(query) {
if (!hasMatchMedia()) {
return;
}
return matchMedia(query);
}
function safeMutationObserver(callback) {
if (typeof MutationObserver === "undefined") {
return;
}
return new MutationObserver(callback);
}
function isInArray(value, array) {
return value === array || (isArray(array) && array.includes(value));
}
function itemFromArray(array, index, useIndex = true) {
return array[index !== undefined && useIndex ? index % array.length : Math.floor(getRandom() * array.length)];
}
function isPointInside(point, size, offset, radius, direction) {
return areBoundsInside(calculateBounds(point, radius ?? minRadius$7), size, offset, direction);
}
function areBoundsInside(bounds, size, offset, direction) {
let inside = true;
if (!direction || direction === exports.OutModeDirection.bottom) {
inside = bounds.top < size.height + offset.x;
}
if (inside && (!direction || direction === exports.OutModeDirection.left)) {
inside = bounds.right > offset.x;
}
if (inside && (!direction || direction === exports.OutModeDirection.right)) {
inside = bounds.left < size.width + offset.y;
}
if (inside && (!direction || direction === exports.OutModeDirection.top)) {
inside = bounds.bottom > offset.y;
}
return inside;
}
function calculateBounds(point, radius) {
return {
bottom: point.y + radius,
left: point.x - radius,
right: point.x + radius,
top: point.y - radius,
};
}
function deepExtend(destination, ...sources) {
for (const source of sources) {
if (isNull(source)) {
continue;
}
if (!isObject(source)) {
destination = source;
continue;
}
if (Array.isArray(source)) {
if (!Array.isArray(destination)) {
destination = [];
}
}
else if (!isObject(destination) || Array.isArray(destination)) {
destination = Object.create(null);
}
const sourceKeys = Object.keys(source), hasNested = sourceKeys.some(k => {
const v = source[k];
return isObject(v) || Array.isArray(v);
});
if (!hasNested) {
const sourceDict = source, destDict = destination;
for (const key of sourceKeys) {
if (isForbiddenKey(key)) {
continue;
}
const v = sourceDict[key];
if (v !== undefined) {
destDict[key] = v;
}
}
continue;
}
for (const key of sourceKeys) {
if (isForbiddenKey(key)) {
continue;
}
const sourceDict = source, destDict = destination, value = sourceDict[key];
destDict[key] = Array.isArray(value)
? value.map(v => deepExtend(undefined, v))
: deepExtend(destDict[key], value);
}
}
return destination;
}
function circleBounceDataFromParticle(p) {
return {
position: p.getPosition(),
radius: p.getRadius(),
mass: p.getMass(),
velocity: p.velocity,
factor: Vector.create(getRangeValue(p.options.bounce.horizontal.value), getRangeValue(p.options.bounce.vertical.value)),
};
}
function circleBounce(p1, p2) {
const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity), [pos1, pos2] = [p1.position, p2.position], { dx: xDist, dy: yDist } = getDistances(pos2, pos1), minimumDistance = 0;
if (xVelocityDiff * xDist + yVelocityDiff * yDist < minimumDistance) {
return;
}
const angle = -Math.atan2(yDist, xDist), m1 = p1.mass, m2 = p2.mass, u1 = p1.velocity.rotate(angle), u2 = p2.velocity.rotate(angle), v1 = collisionVelocity(u1, u2, m1, m2), v2 = collisionVelocity(u2, u1, m1, m2), vFinal1 = v1.rotate(-angle), vFinal2 = v2.rotate(-angle);
p1.velocity.x = vFinal1.x * p1.factor.x;
p1.velocity.y = vFinal1.y * p1.factor.y;
p2.velocity.x = vFinal2.x * p2.factor.x;
p2.velocity.y = vFinal2.y * p2.factor.y;
}
function executeOnSingleOrMultiple(obj, callback) {
const defaultIndex = 0;
return isArray(obj) ? obj.map((item, index) => callback(item, index)) : callback(obj, defaultIndex);
}
function itemFromSingleOrMultiple(obj, index, useIndex) {
return isArray(obj) ? itemFromArray(obj, index, useIndex) : obj;
}
function getPositionOrSize(positionOrSize, canvasSize) {
const isPercent = positionOrSize.mode === exports.PixelMode.percent;
if (!isPercent) {
const { mode: _, ...rest } = positionOrSize;
return rest;
}
const isPosition = "x" in positionOrSize;
if (isPosition) {
return {
x: (positionOrSize.x / percentDenominator) * canvasSize.width,
y: (positionOrSize.y / percentDenominator) * canvasSize.height,
};
}
else {
return {
width: (positionOrSize.width / percentDenominator) * canvasSize.width,
height: (positionOrSize.height / percentDenominator) * canvasSize.height,
};
}
}
function getPosition(position, canvasSize) {
return getPositionOrSize(position, canvasSize);
}
function cloneStyle(style) {
const clonedStyle = safeDocument().createElement("div").style;
for (const key in style) {
const styleKey = style[key];
if (!(key in style) || isNull(styleKey)) {
continue;
}
const styleValue = style.getPropertyValue?.(styleKey);
if (!styleValue) {
continue;
}
const stylePriority = style.getPropertyPriority?.(styleKey);
if (stylePriority) {
clonedStyle.setProperty(styleKey, styleValue, stylePriority);
}
else {
clonedStyle.setProperty(styleKey, styleValue);
}
}
return clonedStyle;
}
let _cachedZIndex, _cachedStyle;
function getFullScreenStyle(zIndex) {
if (_cachedZIndex !== zIndex || !_cachedStyle) {
_cachedZIndex = zIndex;
const fullScreenStyle = safeDocument().createElement("div").style, radix = 10, style = {
width: "100%",
height: "100%",
margin: "0",
padding: "0",
borderWidth: "0",
position: "fixed",
zIndex: zIndex.toString(radix),
"z-index": zIndex.toString(radix),
top: "0",
left: "0",
"pointer-events": "none",
};
for (const key in style) {
const value = style[key];
if (value === undefined) {
continue;
}
fullScreenStyle.setProperty(key, value);
}
_cachedStyle = fullScreenStyle;
}
return _cachedStyle;
}
function manageListener(element, event, handler, add, options) {
if (add) {
let addOptions = { passive: true };
if (isBoolean(options)) {
addOptions.capture = options;
}
else if (options !== undefined) {
addOptions = options;
}
element.addEventListener(event, handler, addOptions);
}
else {
const removeOptions = options;
element.removeEventListener(event, handler, removeOptions);
}
}
async function getItemsFromInitializer(container, map, initializers, force = false) {
let res = map.get(container);
if (!res || force) {
res = await Promise.all([...initializers.values()].map(t => t(container)));
map.set(container, res);
}
return res;
}
async function getItemMapFromInitializer(container, map, initializers, force = false) {
let res = map.get(container);
if (!res || force) {
const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
res = new Map(entries);
map.set(container, res);
}
return res;
}
class EventDispatcher {
#listeners;
constructor() {
this.#listeners = new Map();
}
addEventListener(type, listener) {
this.removeEventListener(type, listener);
let arr = this.#listeners.get(type);
if (!arr) {
arr = [];
this.#listeners.set(type, arr);
}
arr.push(listener);
}
dispatchEvent(type, args) {
const listeners = this.#listeners.get(type);
listeners?.forEach(handler => {
handler(args);
});
}
hasEventListener(type) {
return !!this.#listeners.get(type);
}
removeAllEventListeners(type) {
if (!type) {
this.#listeners = new Map();
}
else {
this.#listeners.delete(type);
}
}
removeEventListener(type, listener) {
const arr = this.#listeners.get(type);
if (!arr) {
return;
}
const length = arr.length, idx = arr.indexOf(listener);
if (idx < minIndex$1) {
return;
}
if (length === deleteCount) {
this.#listeners.delete(type);
}
else {
arr.splice(idx, deleteCount);
}
}
}
exports.EventType = void 0;
(function (EventType) {
EventType["configAdded"] = "configAdded";
EventType["containerInit"] = "containerInit";
EventType["particlesSetup"] = "particlesSetup";
EventType["containerStarted"] = "containerStarted";
EventType["containerStopped"] = "containerStopped";
EventType["containerDestroyed"] = "containerDestroyed";
EventType["containerPaused"] = "containerPaused";
EventType["containerPlay"] = "containerPlay";
EventType["containerBuilt"] = "containerBuilt";
EventType["particleAdded"] = "particleAdded";
EventType["particleDestroyed"] = "particleDestroyed";
EventType["particleRemoved"] = "particleRemoved";
})(exports.EventType || (exports.EventType = {}));
class PluginManager {
colorManagers = new Map();
easingFunctions = new Map();
effectDrawers = new Map();
initializers = {
effects: new Map(),
shapes: new Map(),
updaters: new Map(),
};
palettes = new Map();
plugins = [];
presets = new Map();
shapeDrawers = new Map();
updaters = new Map();
#allLoadersSet = new Set();
#configs = new Map();
#engine;
#executedSet = new Set();
#initialized = false;
#isRunningLoaders = false;
#loadPromises = new Set();
constructor(engine) {
this.#engine = engine;
}
get configs() {
const res = {};
for (const [name, config] of this.#configs) {
res[name] = config;
}
return res;
}
addColorManager(name, manager) {
this.colorManagers.set(name, manager);
}
addConfig(config) {
const key = config.key ?? config.name ?? "default";
this.#configs.set(key, config);
this.#engine.dispatchEvent(exports.EventType.configAdded, { data: { name: key, config } });
}
addEasing(name, easing) {
if (this.easingFunctions.get(name)) {
return;
}
this.easingFunctions.set(name, easing);
}
addEffect(effect, drawer) {
this.initializers.effects.set(effect, drawer);
}
addPalette(name, palette) {
this.palettes.set(name, palette);
}
addParticleUpdater(name, updaterInitializer) {
this.initializers.updaters.set(name, updaterInitializer);
}
addPlugin(plugin) {
if (this.getPlugin(plugin.id)) {
return;
}
this.plugins.push(plugin);
}
addPreset(preset, options, override = false) {
if (!(override || !this.getPreset(preset))) {
return;
}
this.presets.set(preset, options);
}
addShape(shapes, drawer) {
for (const shape of shapes) {
this.initializers.shapes.set(shape, drawer);
}
}
clearPlugins(container) {
this.effectDrawers.delete(container);
this.shapeDrawers.delete(container);
this.updaters.delete(container);
}
getEasing(name) {
return this.easingFunctions.get(name) ?? ((value) => value);
}
getEffectDrawers(container, force = false) {
return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
}
getPalette(name) {
return this.palettes.get(name);
}
getPlugin(plugin) {
return this.plugins.find(t => t.id === plugin);
}
getPreset(preset) {
return this.presets.get(preset);
}
async getShapeDrawers(container, force = false) {
return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
}
async getUpdaters(container, force = false) {
return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
}
async init() {
if (this.#initialized || this.#isRunningLoaders) {
return;
}
this.#isRunningLoaders = true;
this.#executedSet = new Set();
this.#allLoadersSet = new Set(this.#loadPromises);
try {
for (const loader of this.#allLoadersSet) {
await this.#runLoader(loader, this.#executedSet, this.#allLoadersSet);
}
}
finally {
this.#loadPromises.clear();
this.#isRunningLoaders = false;
this.#initialized = true;
}
}
loadParticlesOptions(container, options, ...sourceOptions) {
const updaters = this.updaters.get(container);
if (!updaters) {
return;
}
updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
}
async register(...loaders) {
if (this.#initialized) {
throw new Error("Register plugins can only be done before calling tsParticles.load()");
}
for (const loader of loaders) {
if (this.#isRunningLoaders) {
await this.#runLoader(loader, this.#executedSet, this.#allLoadersSet);
}
else {
this.#loadPromises.add(loader);
}
}
}
async #runLoader(loader, executed, allLoaders) {
if (executed.has(loader))
return;
executed.add(loader);
allLoaders.add(loader);
await loader(this.#engine);
}
}
const errorPrefix = "tsParticles - Error";
const wrap = (fn) => (...args) => {
fn(...args);
}, _logger = {
debug: wrap(console.debug),
error: (message, ...optionalParams) => {
console.error(`${errorPrefix} - ${message}`, ...optionalParams);
},
info: wrap(console.info),
log: wrap(console.log),
trace: wrap(console.trace),
verbose: wrap(console.log),
warning: wrap(console.warn),
};
function setLogger(logger) {
if (logger.debug) {
_logger.debug = wrap(logger.debug);
}
if (logger.error) {
_logger.error = wrap(logger.error);
}
if (logger.info) {
_logger.info = wrap(logger.info);
}
if (logger.log) {
_logger.log = wrap(logger.log);
}
if (logger.trace) {
_logger.trace = wrap(logger.trace);
}
if (logger.verbose) {
_logger.verbose = wrap(logger.verbose);
}
if (logger.warning) {
_logger.warning = wrap(logger.warning);
}
}
function getLogger() {
return _logger;
}
const fullPercent = "100%";
async function getDataFromUrl(data) {
const url = itemFromSingleOrMultiple(data.url, data.index);
if (!url) {
return data.fallback;
}
const response = await fetch(url);
if (response.ok) {
return (await response.json());
}
getLogger().error(`${response.status.toString()} while retrieving config file`);
return data.fallback;
}
const getCanvasFromContainer = (domContainer) => {
const documentSafe = safeDocument();
let canvasEl;
const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
if (isCanvas) {
canvasEl = domContainer;
canvasEl.dataset[generatedAttribute] ??= generatedFalse;
if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
canvasEl.style.width ||= fullPercent;
canvasEl.style.height ||= fullPercent;
canvasEl.style.pointerEvents = "none";
canvasEl.style.setProperty("pointer-events", "none");
}
}
else {
const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
if (foundCanvas) {
canvasEl = foundCanvas;
canvasEl.dataset[generatedAttribute] = generatedFalse;
}
else {
canvasEl = documentSafe.createElement(canvasTag);
canvasEl.dataset[generatedAttribute] = generatedTrue;
domContainer.appendChild(canvasEl);
}
canvasEl.style.width ||= fullPercent;
canvasEl.style.height ||= fullPercent;
canvasEl.style.pointerEvents = "none";
canvasEl.style.setProperty("pointer-events", "none");
}
return canvasEl;
}, getDomContainer = (id, source) => {
const documentSafe = safeDocument();
let domContainer = source ?? documentSafe.getElementById(id);
if (domContainer) {
return domContainer;
}
domContainer = documentSafe.createElement("canvas");
domContainer.id = id;
domContainer.dataset[generatedAttribute] = generatedTrue;
documentSafe.body.append(domContainer);
return domContainer;
};
class Engine {
pluginManager = new PluginManager(this);
#domArray = [];
#eventDispatcher = new EventDispatcher();
#initialized = false;
get items() {
return this.#domArray;
}
get version() {
return "4.4.0";
}
addEventListener(type, listener) {
this.#eventDispatcher.addEventListener(type, listener);
}
checkVersion(pluginVersion) {
if (this.version === pluginVersion) {
return;
}
throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
}
dispatchEvent(type, args) {
this.#eventDispatcher.dispatchEvent(type, args);
}
async init() {
if (this.#initialized) {
return;
}
await this.pluginManager.init();
this.#initialized = true;
}
item(index) {
const items = this.items, item = items[index];
if (item?.destroyed) {
items.splice(index, removeDeleteCount);
return;
}
return item;
}
async load(params) {
await this.init();
let domSourceElement;
if (typeof HTMLElement !== "undefined" && params.element instanceof HTMLElement) {
domSourceElement = params.element;
}
const { Container } = await Promise.resolve().then(function () { return Container$1; }), id = params.id ?? domSourceElement?.id ?? `tsparticles${Math.floor(getRandom() * loadRandomFactor).toString()}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options, currentOptions = itemFromSingleOrMultiple(options, index), { items } = this, oldIndex = items.findIndex(v => v.id.description === id), newItem = new Container({
dispatchCallback: (eventType, args) => {
this.dispatchEvent(eventType, args);
},
id,
onDestroy: (remove) => {
if (!remove) {
return;
}
const mainArr = this.items, idx = mainArr.indexOf(newItem);
if (idx >= removeMinIndex) {
mainArr.splice(idx, removeDeleteCount);
}
},
pluginManager: this.pluginManager,
sourceOptions: currentOptions,
});
if (oldIndex >= loadMinIndex) {
const old = this.item(oldIndex), deleteCount = old ? one : none;
if (old && !old.destroyed) {
old.destroy(false);
}
items.splice(oldIndex, deleteCount, newItem);
}
else {
items.push(newItem);
}
const sourceCanvas = typeof OffscreenCanvas !== "undefined" && params.element instanceof OffscreenCanvas
? params.element
: getCanvasFromContainer(getDomContainer(id, domSourceElement));
newItem.canvas.loadCanvas(sourceCanvas);
await newItem.start();
return newItem;
}
async refresh(refresh = true) {
if (!refresh) {
return;
}
await Promise.all(this.items.map(t => t.refresh()));
}
removeEventListener(type, listener) {
this.#eventDispatcher.removeEventListener(type, listener);
}
}
function initEngine() {
const existing = globalThis.tsParticles;
if (existing?.pluginManager) {
return existing;
}
return new Engine();
}
var RangeType;
(function (RangeType) {
RangeType["circle"] = "circle";
RangeType["rectangle"] = "rectangle";
})(RangeType || (RangeType = {}));
class BaseRange {
position;
type;
constructor(x, y, type) {
this.position = {
x: x,
y: y,
};
this.type = type;
}
_resetPosition(x, y) {
this.position.x = x;
this.position.y = y;
}
}
class Circle extends BaseRange {
radius;
constructor(x, y, radius) {
super(x, y, RangeType.circle);
this.radius = radius;
}
contains(point) {
return checkDistance(point, this.position, this.radius);
}
intersects(range) {
const pos1 = this.position, pos2 = range.position, r = this.radius, dx = Math.abs(pos2.x - pos1.x), dy = Math.abs(pos2.y - pos1.y);
if (range instanceof Circle || range.type === RangeType.circle) {
const circleRange = range, rSum = r + circleRange.radius, dist = Math.hypot(dx, dy);
return rSum > dist;
}
else if (range instanceof Rectangle || range.type === RangeType.rectangle) {
const rectRange = range, { width, height } = rectRange.size, edges = Math.pow(dx - width, squareExp) + Math.pow(dy - height, squareExp);
return edges <= r ** squareExp || (dx <= r + width && dy <= r + height) || dx <= width || dy <= height;
}
return false;
}
reset(x, y, radius) {
this._resetPosition(x, y);
this.radius = radius;
return this;
}
}
class Rectangle extends BaseRange {
size;
constructor(x, y, width, height) {
super(x, y, RangeType.rectangle);
this.size = {
height: height,
width: width,
};
}
contains(point) {
const w = this.size.width, h = this.size.height, pos = this.position;
return point.x >= pos.x && point.x <= pos.x + w && point.y >= pos.y && point.y <= pos.y + h;
}
intersects(range) {
if (range instanceof Circle) {
return range.intersects(this);
}
if (!(range instanceof Rectangle)) {
return false;
}
const w = this.size.width, h = this.size.height, pos1 = this.position, pos2 = range.position, size2 = range.size, w2 = size2.width, h2 = size2.height;
return pos2.x < pos1.x + w && pos2.x + w2 > pos1.x && pos2.y < pos1.y + h && pos2.y + h2 > pos1.y;
}
reset(x, y, width, height) {
this._resetPosition(x, y);
this.size.width = width;
this.size.height = height;
return this;
}
}
exports.RotateDirection = void 0;
(function (RotateDirection) {
RotateDirection["clockwise"] = "clockwise";
RotateDirection["counterClockwise"] = "counter-clockwise";
RotateDirection["random"] = "random";
})(exports.RotateDirection || (exports.RotateDirection = {}));
exports.AnimationMode = void 0;
(function (AnimationMode) {
AnimationMode["auto"] = "auto";
AnimationMode["increase"] = "increase";
AnimationMode["decrease"] = "decrease";
AnimationMode["random"] = "random";
})(exports.AnimationMode || (exports.AnimationMode = {}));
exports.HdrMode = void 0;
(function (HdrMode) {
HdrMode["standard"] = "standard";
HdrMode["natural"] = "natural";
HdrMode["vivid"] = "vivid";
HdrMode["cinematic"] = "cinematic";
HdrMode["dynamic"] = "dynamic";
})(exports.HdrMode || (exports.HdrMode = {}));
exports.LimitMode = void 0;
(function (LimitMode) {
LimitMode["delete"] = "delete";
LimitMode["wait"] = "wait";
})(exports.LimitMode || (exports.LimitMode = {}));
exports.OutMode = void 0;
(function (OutMode) {
OutMode["bounce"] = "bounce";
OutMode["none"] = "none";
OutMode["out"] = "out";
OutMode["destroy"] = "destroy";
OutMode["split"] = "split";
})(exports.OutMode || (exports.OutMode = {}));
exports.AlterType = void 0;
(function (AlterType) {
AlterType["darken"] = "darken";
AlterType["enlighten"] = "enlighten";
})(exports.AlterType || (exports.AlterType = {}));
exports.DestroyType = void 0;
(function (DestroyType) {
DestroyType["none"] = "none";
DestroyType["max"] = "max";
DestroyType["min"] = "min";
})(exports.DestroyType || (exports.DestroyType = {}));
exports.GradientType = void 0;
(function (GradientType) {
GradientType["linear"] = "linear";
GradientType["radial"] = "radial";
GradientType["random"] = "random";
})(exports.GradientType || (exports.GradientType = {}));
exports.ParticleOutType = void 0;
(function (ParticleOutType) {
ParticleOutType["normal"] = "normal";
ParticleOutType["inside"] = "inside";
ParticleOutType["outside"] = "outside";
})(exports.ParticleOutType || (exports.ParticleOutType = {}));
exports.StartValueType = void 0;
(function (StartValueType) {
StartValueType["max"] = "max";
StartValueType["min"] = "min";
StartValueType["random"] = "random";
})(exports.StartValueType || (exports.StartValueType = {}));
exports.AnimationStatus = void 0;
(function (AnimationStatus) {
AnimationStatus["increasing"] = "increasing";
AnimationStatus["decreasing"] = "decreasing";
})(exports.AnimationStatus || (exports.AnimationStatus = {}));
exports.DrawLayer = void 0;
(function (DrawLayer) {
DrawLayer[DrawLayer["BackgroundElement"] = 0] = "BackgroundElement";
DrawLayer[DrawLayer["BackgroundDraw"] = 1] = "BackgroundDraw";
DrawLayer[DrawLayer["BackgroundMask"] = 2] = "BackgroundMask";
DrawLayer[DrawLayer["CanvasSetup"] = 3] = "CanvasSetup";
DrawLayer[DrawLayer["PluginContent"] = 4] = "PluginContent";
DrawLayer[DrawLayer["Particles"] = 5] = "Particles";
DrawLayer[DrawLayer["CanvasCleanup"] = 6] = "CanvasCleanup";
DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
})(exports.DrawLayer || (exports.DrawLayer = {}));
class OptionLoader {
load(data) {
if (isNull(data)) {
return;
}
this.doLoad(data);
}
}
function loadOptions(options, ...sourceOptionsArr) {
for (const sourceOptions of sourceOptionsArr) {
options.load(sourceOptions);
}
}
function loadProperty(obj, key, value) {
if (value !== undefined) {
obj[key] = value;
}
}
function loadRangeProperty(obj, key, value) {
if (value !== undefined) {
obj[key] = setRangeValue(value);
}
}
function loadNestedProperty(obj, key, value) {
if (value !== undefined) {
obj[key].load(value);
}
}
function loadLazyProperty(obj, key, value, factory) {
if (value !== undefined) {
const objRecord = obj;
objRecord[key] ??= factory();
objRecord[key].load(value);
}
}
function loadExtendProperty(obj, key, value) {
if (value !== undefined) {
const keyStr = key;
if (keyStr === "__proto__" || keyStr === "constructor" || keyStr === "prototype") {
return;
}
const objRecord = obj, currentValue = Object.prototype.hasOwnProperty.call(objRecord, keyStr) ? objRecord[keyStr] : {};
objRecord[keyStr] = deepExtend(currentValue, value);
}
}
function loadOptionProperty(obj, key, optionClass, ...sources) {
const objRecord = obj;
objRecord[key] ??= new optionClass();
const target = objRecord[key];
for (const source of sources) {
target.load(source?.[key]);
}
}
class AnimationOptions extends OptionLoader {
count = 0;
decay = 0;
delay = 0;
enable = false;
speed = 1;
sync = false;
doLoad(data) {
loadRangeProperty(this, "count", data.count);
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "decay", data.decay);
loadRangeProperty(this, "delay", data.delay);
loadProperty(this, "sync", data.sync);
}
}
class RangedAnimationOptions extends AnimationOptions {
mode = exports.AnimationMode.auto;
startValue = exports.StartValueType.random;
doLoad(data) {
super.doLoad(data);
loadProperty(this, "mode", data.mode);
loadProperty(this, "startValue", data.startValue);
}
}
class ColorAnimation extends AnimationOptions {
max;
min;
offset = 0;
sync = true;
constructor(min, max) {
super();
this.min = min;
this.max = max;
}
doLoad(data) {
super.doLoad(data);
loadProperty(this, "max", data.max);
loadProperty(this, "min", data.min);
loadRangeProperty(this, "offset", data.offset);
}
}
class HslAnimation extends OptionLoader {
h = new ColorAnimation(hMin, hMax);
l = new ColorAnimation(lMin, lMax);
s = new ColorAnimation(sMin, sMax);
doLoad(data) {
this.h.load(data.h);
this.s.load(data.s);
this.l.load(data.l);
}
}
class OptionsColor extends OptionLoader {
value = "";
static create(source, data) {
const color = new OptionsColor();
color.load(source);
if (data !== undefined) {
if (isString(data) || isArray(data)) {
color.load({ value: data });
}
else {
color.load(data);
}
}
return color;
}
doLoad(data) {
if (!isNull(data.value)) {
this.value = data.value;
}
}
}
class AnimatableColor extends OptionsColor {
animation = new HslAnimation();
static create(source, data) {
const color = new AnimatableColor();
color.load(source);
if (data !== undefined) {
if (isString(data) || isArray(data)) {
color.load({ value: data });
}
else {
color.load(data);
}
}
return color;
}
doLoad(data) {
super.doLoad(data);
const colorAnimation = data.animation;
if (colorAnimation !== undefined) {
if (colorAnimation.enable === undefined) {
this.animation.load(data.animation);
}
else {
this.animation.h.load(colorAnimation);
}
}
}
}
class Background extends OptionLoader {
color;
draw;
element;
image = "";
opacity = 1;
position = "";
repeat = "";
size = "";
constructor() {
super();
this.color = new OptionsColor();
this.color.value = "";
}
doLoad(data) {
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "element", data.element);
loadProperty(this, "draw", data.draw);
loadProperty(this, "image", data.image);
loadProperty(this, "position", data.position);
loadProperty(this, "repeat", data.repeat);
loadProperty(this, "size", data.size);
loadProperty(this, "opacity", data.opacity);
}
}
class FullScreen extends OptionLoader {
enable = true;
zIndex = 0;
doLoad(data) {
loadProperty(this, "enable", data.enable);
loadProperty(this, "zIndex", data.zIndex);
}
}
const defaultPeakNits = 400, minPeakNits = 0, toStringValue = (value) => String(value);
class HDROptions extends OptionLoader {
enable = true;
mode = exports.HdrMode.standard;
peakNits = defaultPeakNits;
doLoad(data) {
if (data.enable !== undefined && !isBoolean(data.enable)) {
throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
}
if (data.mode !== undefined &&
(typeof data.mode !== "string" || !Object.values(exports.HdrMode).includes(data.mode))) {
throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(exports.HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
}
if (data.peakNits !== undefined &&
(!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "mode", data.mode);
loadProperty(this, "peakNits", data.peakNits);
}
}
class ResizeEvent extends OptionLoader {
delay = 0.5;
enable = true;
doLoad(data) {
loadProperty(this, "delay", data.delay);
loadProperty(this, "enable", data.enable);
}
}
class Effect extends OptionLoader {
close = true;
options = {};
type = [];
doLoad(data) {
const options = data.options;
if (options !== undefined) {
for (const effect in options) {
const item = options[effect];
if (item) {
this.options[effect] = deepExtend(this.options[effect] ?? {}, item);
}
}
}
loadProperty(this, "close", data.close);
loadProperty(this, "type", data.type);
}
}
class Fill extends OptionLoader {
color;
enable = true;
opacity = 1;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class MoveAngle extends OptionLoader {
offset = 0;
value = 90;
doLoad(data) {
loadRangeProperty(this, "offset", data.offset);
loadRangeProperty(this, "value", data.value);
}
}
class MoveCenter extends OptionLoader {
mode = exports.PixelMode.percent;
radius = 0;
x = 50;
y = 50;
doLoad(data) {
loadProperty(this, "x", data.x);
loadProperty(this, "y", data.y);
loadProperty(this, "mode", data.mode);
loadProperty(this, "radius", data.radius);
}
}
class MoveGravity extends OptionLoader {
acceleration = 9.81;
enable = false;
inverse = false;
maxSpeed = 50;
doLoad(data) {
loadRangeProperty(this, "acceleration", data.acceleration);
loadProperty(this, "enable", data.enable);
loadProperty(this, "inverse", data.inverse);
loadRangeProperty(this, "maxSpeed", data.maxSpeed);
}
}
class ValueWithRandom extends OptionLoader {
value = 0;
doLoad(data) {
if (!isNull(data.value)) {
this.value = setRangeValue(data.value);
}
}
}
class AnimationValueWithRandom extends ValueWithRandom {
animation = new AnimationOptions();
doLoad(data) {
super.doLoad(data);
loadNestedProperty(this, "animation", data.animation);
}
}
class RangedAnimationValueWithRandom extends AnimationValueWithRandom {
animation = new RangedAnimationOptions();
}
class MovePath extends OptionLoader {
clamp = true;
delay = new ValueWithRandom();
enable = false;
generator;
options = {};
doLoad(data) {
loadProperty(this, "clamp", data.clamp);
this.delay.load(data.delay);
loadProperty(this, "enable", data.enable);
this.generator = data.generator;
if (data.options) {
this.options = deepExtend(this.options, data.options);
}
}
}
class OutModes extends OptionLoader {
bottom;
default = exports.OutMode.out;
left;
right;
top;
doLoad(data) {
if (data.default !== undefined) {
this.default = data.default;
}
this.bottom = data.bottom ?? data.default;
this.left = data.left ?? data.default;
this.right = data.right ?? data.default;
this.top = data.top ?? data.default;
}
}
class Spin extends OptionLoader {
acceleration = 0;
enable = false;
position;
doLoad(data) {
loadRangeProperty(this, "acceleration", data.acceleration);
loadProperty(this, "enable", data.enable);
if (data.position) {
this.position = deepExtend({}, data.position);
}
}
}
class Move extends OptionLoader {
angle = new MoveAngle();
center = new MoveCenter();
decay = 0;
direction = exports.MoveDirection.none;
distance = {};
drift = 0;
enable = false;
gravity = new MoveGravity();
outModes = new OutModes();
path = new MovePath();
random = false;
size = false;
speed = 2;
spin = new Spin();
straight = false;
vibrate = false;
warp = false;
doLoad(data) {
this.angle.load(isNumber(data.angle) ? { value: data.angle } : data.angle);
this.center.load(data.center);
loadRangeProperty(this, "decay", data.decay);
loadProperty(this, "direction", data.direction);
if (data.distance !== undefined) {
this.distance = isNumber(data.distance)
? {
horizontal: data.distance,
vertical: data.distance,
}
: { ...data.distance };
}
loadRangeProperty(this, "drift", data.drift);
loadProperty(this, "enable", data.enable);
this.gravity.load(data.gravity);
const outModes = data.outModes;
if (outModes !== undefined) {
if (isObject(outModes)) {
this.outModes.load(outModes);
}
else {
this.outModes.load({
default: outModes,
});
}
}
this.path.load(data.path);
loadProperty(this, "random", data.random);
loadProperty(this, "size", data.size);
loadRangeProperty(this, "speed", data.speed);
this.spin.load(data.spin);
loadProperty(this, "straight", data.straight);
loadProperty(this, "vibrate", data.vibrate);
loadProperty(this, "warp", data.warp);
}
}
class Stroke extends OptionLoader {
color;
opacity;
width = 0;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadRangeProperty(this, "width", data.width);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class Paint extends OptionLoader {
color;
fill;
stroke;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadLazyProperty(this, "fill", data.fill, () => new Fill());
loadLazyProperty(this, "stroke", data.stroke, () => new Stroke());
}
}
class ParticlesBounceFactor extends ValueWithRandom {
value = 1;
}
class ParticlesBounce extends OptionLoader {
horizontal = new ParticlesBounceFactor();
vertical = new ParticlesBounceFactor();
doLoad(data) {
this.horizontal.load(data.horizontal);
this.vertical.load(data.vertical);
}
}
class ParticlesDensity extends OptionLoader {
enable = false;
height = 1080;
width = 1920;
doLoad(data) {
loadProperty(this, "enable", data.enable);
loadProperty(this, "width", data.width);
loadProperty(this, "height", data.height);
}
}
class ParticlesNumberLimit extends OptionLoader {
mode = exports.LimitMode.delete;
value = 0;
doLoad(data) {
loadProperty(this, "mode", data.mode);
loadProperty(this, "value", data.value);
}
}
class ParticlesNumber extends OptionLoader {
density = new ParticlesDensity();
limit = new ParticlesNumberLimit();
value = 0;
doLoad(data) {
this.density.load(data.density);
this.limit.load(data.limit);
loadProperty(this, "value", data.value);
}
}
class Shape extends OptionLoader {
close = true;
options = {};
type = "circle";
doLoad(data) {
const options = data.options;
if (options !== undefined) {
for (const shape in options) {
const item = options[shape];
if (item) {
this.options[shape] = deepExtend(this.options[shape] ?? {}, item);
}
}
}
loadProperty(this, "close", data.close);
loadProperty(this, "type", data.type);
}
}
class ZIndex extends ValueWithRandom {
opacityRate = 1;
sizeRate = 1;
velocityRate = 1;
doLoad(data) {
super.doLoad(data);
loadProperty(this, "opacityRate", data.opacityRate);
loadProperty(this, "sizeRate", data.sizeRate);
loadProperty(this, "velocityRate", data.velocityRate);
}
}
class ParticlesOptions extends OptionLoader {
bounce = new ParticlesBounce();
effect = new Effect();
groups = {};
move = new Move();
number = new ParticlesNumber();
paint;
palette;
reduceDuplicates = false;
shape = new Shape();
zIndex = new ZIndex();
#container;
#pluginManager;
constructor(pluginManager, container) {
super();
this.#pluginManager = pluginManager;
this.#container = container;
this.paint = new Paint();
this.paint.color = new AnimatableColor();
this.paint.color.value = "#fff";
this.paint.fill = new Fill();
this.paint.fill.enable = true;
}
doLoad(data) {
if (data.palette) {
this.palette = data.palette;
this.#importPalette(this.palette);
}
if (data.groups !== undefined) {
for (const group of Object.keys(data.groups)) {
if (!(group in data.groups)) {
continue;
}
const item = data.groups[group];
if (item !== undefined) {
this.groups[group] = deepExtend(this.groups[group] ?? {}, item);
}
}
}
if (data.reduceDuplicates !== undefined) {
this.reduceDuplicates = data.reduceDuplicates;
}
this.bounce.load(data.bounce);
this.effect.load(data.effect);
this.move.load(data.move);
this.number.load(data.number);
const paintToLoad = data.paint;
if (paintToLoad) {
if (isArray(paintToLoad)) {
this.paint = executeOnSingleOrMultiple(paintToLoad, t => {
const tmp = new Paint();
tmp.load(t);
return tmp;
});
}
else if (isArray(this.paint)) {
this.paint = new Paint();
this.paint.load(paintToLoad);
}
else {
this.paint.load(paintToLoad);
}
}
this.shape.load(data.shape);
this.zIndex.load(data.zIndex);
if (this.#container) {
for (const plugin of this.#pluginManager.plugins) {
if (plugin.loadParticlesOptions) {
plugin.loadParticlesOptions(this.#container, this, data);
}
}
const updaters = this.#pluginManager.updaters.get(this.#container);
if (updaters) {
for (const updater of updaters) {
if (updater.loadOptions) {
updater.loadOptions(this, data);
}
}
}
}
}
#importPalette(palette) {
const paletteData = this.#pluginManager.getPalette(palette);
if (!paletteData) {
return;
}
const paletteColors = paletteData.colors, defaultPaintStrokeWidth = 0, defaultPaintVariantsLength = 1, firstPaintVariantIndex = 0, defaultPalettePaintVariant = {}, colorVariants = isArray(paletteColors) ? paletteColors : [paletteColors], palettePaintVariants = colorVariants.flatMap(variant => {
const paletteFill = variant.fill, paletteStroke = variant.stroke, fillPart = paletteFill
? {
color: {
value: paletteFill.value,
},
enable: paletteFill.enable,
opacity: paletteFill.opacity,
}
: undefined;
if (!paletteStroke) {
return [
{
fill: fillPart,
},
];
}
return [
{
fill: fillPart,
stroke: {
color: {
value: paletteStroke.value,
},
opacity: paletteStroke.opacity,
width: paletteStroke.width || defaultPaintStrokeWidth,
},
},
];
}), palettePaint = palettePaintVariants.length > defaultPaintVariantsLength
? palettePaintVariants
: (palettePaintVariants[firstPaintVariantIndex] ?? defaultPalettePaintVariant);
this.load({
paint: palettePaint,
blend: {
enable: true,
mode: paletteData.blendMode,
},
});
}
}
function loadParticlesOptions(pluginManager, container, ...sourceOptionsArr) {
const options = new ParticlesOptions(pluginManager, container);
loadOptions(options, ...sourceOptionsArr);
return options;
}
class Options extends OptionLoader {
autoPlay = true;
background;
clear = true;
defaultThemes = {};
delay = 0;
detectRetina = true;
duration = 0;
fpsLimit = 120;
fullScreen;
hdr;
key;
name;
palette;
particles;
pauseOnBlur = true;
pauseOnOutsideViewport = true;
preset;
resize;
smooth = false;
style = {};
zLayers = 100;
#container;
#pluginManager;
constructor(pluginManager, container) {
super();
this.#pluginManager = pluginManager;
this.#container = container;
this.background = new Background();
this.fullScreen = new FullScreen();
this.hdr = new HDROptions();
this.particles = loadParticlesOptions(this.#pluginManager, this.#container);
this.resize = new ResizeEvent();
}
doLoad(data) {
if (data.preset !== undefined) {
this.preset = data.preset;
executeOnSingleOrMultiple(this.preset, preset => {
this.#importPreset(preset);
});
}
if (data.palette !== undefined) {
this.palette = data.palette;
this.#importPalette(this.palette);
}
loadProperty(this, "autoPlay", data.autoPlay);
loadProperty(this, "clear", data.clear);
loadProperty(this, "key", data.key);
loadProperty(this, "name", data.name);
loadRangeProperty(this, "delay", data.delay);
loadProperty(this, "detectRetina", data.detectRetina);
loadRangeProperty(this, "duration", data.duration);
loadProperty(this, "fpsLimit", data.fpsLimit);
const hdrData = data.hdr;
if (isBoolean(hdrData)) {
this.hdr.enable = hdrData;
this.hdr.mode = exports.HdrMode.standard;
this.hdr.peakNits = 400;
}
else {
this.hdr.load(hdrData);
}
loadProperty(this, "pauseOnBlur", data.pauseOnBlur);
loadProperty(this, "pauseOnOutsideViewport", data.pauseOnOutsideViewport);
loadProperty(this, "zLayers", data.zLayers);
this.background.load(data.background);
const fullScreen = data.fullScreen;
if (isBoolean(fullScreen)) {
this.fullScreen.enable = fullScreen;
}
else {
this.fullScreen.load(fullScreen);
}
this.particles.load(data.particles);
this.resize.load(data.resize);
this.style = deepExtend(this.style, data.style);
loadProperty(this, "smooth", data.smooth);
this.#pluginManager.plugins.forEach(plugin => {
plugin.loadOptions(this.#container, this, data);
});
}
#importPalette(palette) {
const paletteData = this.#pluginManager.getPalette(palette);
if (!paletteData) {
return;
}
this.load({
background: {
color: paletteData.background,
},
blend: {
enable: true,
mode: paletteData.blendMode,
},
particles: {
palette,
},
});
}
#importPreset(preset) {
this.load(this.#pluginManager.getPreset(preset));
}
}
const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, hdrRgbFixedPrecision = 4, hdrHslFixedPrecision = 4, sdrReferenceWhiteNits = 203, acesA = 2.51, acesB = 0.03, acesC = 2.43, acesD = 0.59, acesE = 0.14, saturationBoost = 1.15, lightnessBoost = 1.05, temperatureR = 1.05, temperatureB = 0.95, contrastFactor = 1.1, luminanceR = 0.2126, luminanceG = 0.7152, luminanceB = 0.0722, dynamicLuminanceFactor = 2, channelCount = 3, srgbThreshold = 0.04045, srgbDivisor = 12.92, srgbOffset = 0.055, srgbScale = 1.055, srgbExponent = 2.4, linearThreshold = 0.0031308, linearScale = 12.92;
function getCachedStyle(key, generator) {
let cached = styleCache.get(key);
if (!cached) {
cached = generator();
if (styleCache.size > maxStyleCacheSize) {
styleCache.clear();
}
styleCache.set(key, cached);
}
return cached;
}
function acesFilmic(x) {
return clamp((x * (acesA * x + acesB)) / (x * (acesC * x + acesD) + acesE), none, one);
}
function srgbToLinear(c) {
return c <= srgbThreshold ? c / srgbDivisor : Math.pow((c + srgbOffset) / srgbScale, srgbExponent);
}
function linearToSrgb(c) {
return c <= linearThreshold ? c * linearScale : srgbScale * Math.pow(c, one / srgbExponent) - srgbOffset;
}
function applyHdrModeAdjustments(r, g, b, mode, maxChannel) {
switch (mode) {
case exports.HdrMode.vivid: {
const avg = (r + g + b) / channelCount;
return {
b: clamp(clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
g: clamp(clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
r: clamp(clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
};
}
case exports.HdrMode.cinematic: {
const tempR = r * temperatureR, tempB = b * temperatureB, avg = (tempR + g + tempB) / channelCount;
return {
b: clamp(avg + (tempB - avg) * contrastFactor, none, maxChannel),
g: clamp(avg + (g - avg) * contrastFactor, none, maxChannel),
r: clamp(avg + (tempR - avg) * contrastFactor, none, maxChannel),
};
}
case exports.HdrMode.dynamic: {
const luminance = luminanceR * r + luminanceG * g + luminanceB * b, vividWeight = Math.min(one, luminance * dynamicLuminanceFactor), naturalWeight = one - vividWeight, avg = (r + g + b) / channelCount, vividR = clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividG = clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividB = clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost;
return {
b: clamp(b * naturalWeight + vividB * vividWeight, none, maxChannel),
g: clamp(g * naturalWeight + vividG * vividWeight, none, maxChannel),
r: clamp(r * naturalWeight + vividR * vividWeight, none, maxChannel),
};
}
case exports.HdrMode.standard:
return { b, g, r };
case exports.HdrMode.natural:
default:
return { b, g, r };
}
}
function stringToRgba(pluginManager, input) {
if (!input) {
return;
}
for (const manager of pluginManager.colorManagers.values()) {
if (manager.accepts(input)) {
return manager.parseString(input);
}
}
return undefined;
}
function rangeColorToRgb(pluginManager, input, index, useIndex = true) {
if (!input) {
return;
}
const color = isString(input) ? { value: input } : input;
if (isString(color.value)) {
return colorToRgb(pluginManager, color.value, index, useIndex);
}
if (isArray(color.value)) {
const value = itemFromArray(color.value, index, useIndex);
if (!value) {
return;
}
return rangeColorToRgb(pluginManager, {
value,
});
}
for (const manager of pluginManager.colorManagers.values()) {
const res = manager.handleRangeColor(color);
if (res) {
return res;
}
}
return undefined;
}
function colorToRgb(pluginManager, input, index, useIndex = true) {
if (!input) {
return;
}
const color = isString(input) ? { value: input } : input;
if (isString(color.value)) {
return color.value === randomColorValue ? getRandomRgbColor() : stringToRgb(pluginManager, color.value);
}
if (isArray(color.value)) {
const value = itemFromArray(color.value, index, useIndex);
if (!value) {
return;
}
return colorToRgb(pluginManager, {
value,
});
}
for (const manager of pluginManager.colorManagers.values()) {
const res = manager.handleColor(color);
if (res) {
return res;
}
}
return undefined;
}
function rangeColorToHsl(pluginManager, color, index, useIndex = true) {
const rgb = rangeColorToRgb(pluginManager, color, index, useIndex);
return rgb ? rgbToHsl(rgb) : undefined;
}
function rgbToHsl(color) {
const r1 = color.r / rgbMax, g1 = color.g / rgbMax, b1 = color.b / rgbMax, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
h: hMin,
l: (max + min) * half$2,
s: sMin,
};
if (max !== min) {
res.s = res.l < half$2 ? (max - min) / (max + min) : (max - min) / (double - max - min);
if (r1 === max) {
res.h = (g1 - b1) / (max - min);
}
else if (g1 === max) {
res.h = double + (b1 - r1) / (max - min);
}
else {
res.h = double * double + (r1 - g1) / (max - min);
}
}
res.l *= lMax;
res.s *= sMax;
res.h *= hPhase;
if (res.h < hMin) {
res.h += hMax;
}
if (res.h >= hMax) {
res.h -= hMax;
}
return res;
}
function stringToAlpha(pluginManager, input) {
return stringToRgba(pluginManager, input)?.a;
}
function stringToRgb(pluginManager, input) {
return stringToRgba(pluginManager, input);
}
function hslChannel(temp1, temp2, temp3) {
const temp3Min = 0, temp3Max = 1;
if (temp3 < temp3Min) {
temp3++;
}
if (temp3 > temp3Max) {
temp3--;
}
if (temp3 * sextuple < temp3Max) {
return temp1 + (temp2 - temp1) * sextuple * temp3;
}
if (temp3 * double < temp3Max) {
return temp2;
}
if (temp3 * triple < temp3Max * double) {
const temp3Offset = double / triple;
return temp1 + (temp2 - temp1) * (temp3Offset - temp3) * sextuple;
}
return temp1;
}
function hslToRgb(hsl) {
const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
if (s === sMin) {
const grayscaleValue = Math.round(lNormalized * rgbMax);
return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
}
const temp1 = lNormalized < half$2
? lNormalized * (sNormalizedOffset + sNormalized)
: lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
}
function hslToRgbFloat(hsl) {
const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
if (s === sMin) {
const grayscaleValue = lNormalized * rgbMax;
return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
}
const temp1 = lNormalized < half$2
? lNormalized * (sNormalizedOffset + sNormalized)
: lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
return { r: red, g: green, b: blue };
}
function hslaToRgba(hsla) {
const rgbResult = hslToRgb(hsla);
return {
a: hsla.a,
b: rgbResult.b,
g: rgbResult.g,
r: rgbResult.r,
};
}
function getRandomRgbColor(min, hdr) {
const fixedMin = min ?? defaultRgbMin;
if (hdr) {
return {
r: getRandomInRange(fixedMin, rgbMax),
g: getRandomInRange(fixedMin, rgbMax),
b: getRandomInRange(fixedMin, rgbMax),
};
}
const fixedMax = rgbMax + identity$3, getRgbInRangeValue = () => Math.floor(getRandomInRange(fixedMin, fixedMax));
return {
b: getRgbInRangeValue(),
g: getRgbInRangeValue(),
r: getRgbInRangeValue(),
};
}
function getStyleFromRgb(color, hdr, opacity, peakNits, mode) {
const rgbPrecision = hdr ? hdrRgbFixedPrecision : rgbFixedPrecision, op = opacity ?? defaultOpacity$3, key = hdr
? `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? exports.HdrMode.standard}`
: `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-sdr-${op.toString()}`;
return getCachedStyle(key, () => hdr ? getHdrStyleFromRgb(color, opacity, peakNits, mode) : getSdrStyleFromRgb(color, opacity));
}
function getHdrStyleFromRgb(color, opacity, peakNits = maxNits, mode = exports.HdrMode.standard) {
const headroom = peakNits / sdrReferenceWhiteNits, middleGray = 0.18, mapped = hdrToneMapColor(color, headroom, middleGray, mode);
return `color(display-p3 ${mapped.r.toString()} ${mapped.g.toString()} ${mapped.b.toString()} / ${(opacity ?? defaultOpacity$3).toString()})`;
}
function hdrToneMapColor(color, headroom, middleGray, mode) {
const rNorm = color.r / rgbMax, gNorm = color.g / rgbMax, bNorm = color.b / rgbMax;
if (mode !== exports.HdrMode.natural && mode !== exports.HdrMode.vivid && mode !== exports.HdrMode.cinematic && mode !== exports.HdrMode.dynamic) {
return { b: bNorm, g: gNorm, r: rNorm };
}
const maxChannel = Math.max(one, headroom), luminance = luminanceR * rNorm + luminanceG * gNorm + luminanceB * bNorm, mappedLuminance = hdrToneMap(luminance, headroom, middleGray), scale = luminance > none ? mappedLuminance / luminance : one;
return applyHdrModeAdjustments(clamp(rNorm * scale, none, maxChannel), clamp(gNorm * scale, none, maxChannel), clamp(bNorm * scale, none, maxChannel), mode, maxChannel);
}
function hdrToneMap(normalized, headroom, middleGray) {
if (normalized <= middleGray) {
return acesFilmic(normalized);
}
const expanded = normalized + (normalized - middleGray) * (headroom - one);
return acesFilmic(expanded);
}
function getSdrStyleFromRgb(color, opacity) {
return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity$3).toString()})`;
}
function getStyleFromHsl(color, hdr, opacity, peakNits, mode) {
const hslPrecision = hdr ? hdrHslFixedPrecision : hslFixedPrecision, op = opacity ?? defaultOpacity$3, key = hdr
? `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? exports.HdrMode.standard}`
: `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-sdr-${op.toString()}`;
return getCachedStyle(key, () => hdr
? getStyleFromRgb(hslToRgbFloat(color), true, opacity, peakNits, mode)
: `hsla(${color.h.toString()}, ${color.s.toString()}%, ${color.l.toString()}%, ${op.toString()})`);
}
function colorMix(color1, color2, size1, size2, hdr) {
let rgb1 = color1, rgb2 = color2;
if (!("r" in rgb1)) {
rgb1 = hdr ? hslToRgbFloat(color1) : hslToRgb(color1);
}
if (!("r" in rgb2)) {
rgb2 = hdr ? hslToRgbFloat(color2) : hslToRgb(color2);
}
if (hdr) {
const lin1 = {
b: srgbToLinear(rgb1.b / rgbMax),
g: srgbToLinear(rgb1.g / rgbMax),
r: srgbToLinear(rgb1.r / rgbMax),
}, lin2 = {
b: srgbToLinear(rgb2.b / rgbMax),
g: srgbToLinear(rgb2.g / rgbMax),
r: srgbToLinear(rgb2.r / rgbMax),
}, totalWeight = size1 + size2;
return {
b: linearToSrgb((lin1.b * size1 + lin2.b * size2) / totalWeight) * rgbMax,
g: linearToSrgb((lin1.g * size1 + lin2.g * size2) / totalWeight) * rgbMax,
r: linearToSrgb((lin1.r * size1 + lin2.r * size2) / totalWeight) * rgbMax,
};
}
return {
b: mix(rgb1.b, rgb2.b, size1, size2),
g: mix(rgb1.g, rgb2.g, size1, size2),
r: mix(rgb1.r, rgb2.r, size1, size2),
};
}
function getLinkColor(p1, p2, linkColor, hdr) {
if (linkColor === randomColorValue) {
return getRandomRgbColor();
}
else if (linkColor === midColorValue) {
const sourceColor = p1.getFillColor() ?? p1.getStrokeColor(), destColor = p2?.getFillColor() ?? p2?.getStrokeColor();
if (sourceColor && destColor && p2) {
return colorMix(sourceColor, destColor, p1.getRadius(), p2.getRadius(), hdr);
}
else {
const hslColor = sourceColor ?? destColor;
if (hslColor) {
return hslToRgb(hslColor);
}
}
}
else {
return linkColor;
}
return undefined;
}
function getLinkRandomColor(pluginManager, optColor, blink, consent) {
const color = isString(optColor) ? optColor : optColor.value;
if (color === randomColorValue) {
if (consent) {
return rangeColorToRgb(pluginManager, {
value: color,
});
}
if (blink) {
return randomColorValue;
}
return midColorValue;
}
else if (color === midColorValue) {
return midColorValue;
}
else {
return rangeColorToRgb(pluginManager, {
value: color,
});
}
}
function getHslFromAnimation(animation) {
return animation === undefined
? undefined
: {
h: animation.h.value,
s: animation.s.value,
l: animation.l.value,
};
}
function getHslAnimationFromHsl(hsl, animationOptions, reduceFactor) {
const resColor = {
h: {
enable: false,
value: hsl.h,
min: hMin,
max: hMax,
},
s: {
enable: false,
value: hsl.s,
min: sMin,
max: sMax,
},
l: {
enable: false,
value: hsl.l,
min: lMin,
max: lMax,
},
};
if (animationOptions) {
setColorAnimation(resColor.h, animationOptions.h, reduceFactor);
setColorAnimation(resColor.s, animationOptions.s, reduceFactor);
setColorAnimation(resColor.l, animationOptions.l, reduceFactor);
}
return resColor;
}
function setColorAnimation(colorValue, colorAnimation, reduceFactor) {
colorValue.enable = colorAnimation.enable;
colorValue.min = colorAnimation.min;
colorValue.max = colorAnimation.max;
if (colorValue.enable) {
colorValue.velocity = (getRangeValue(colorAnimation.speed) / percentDenominator) * reduceFactor;
colorValue.decay = decayOffset - getRangeValue(colorAnimation.decay);
colorValue.status = exports.AnimationStatus.increasing;
colorValue.loops = defaultLoops;
colorValue.maxLoops = getRangeValue(colorAnimation.count);
colorValue.time = defaultTime;
colorValue.delayTime = getRangeValue(colorAnimation.delay) * millisecondsToSeconds$1;
if (!colorAnimation.sync) {
colorValue.velocity *= getRandom();
colorValue.value *= getRandom();
}
colorValue.initialValue = colorValue.value;
colorValue.offset = setRangeValue(colorAnimation.offset);
}
else {
colorValue.velocity = defaultVelocity;
}
}
function updateColorValue(data, decrease, delta, _hdr) {
const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
if (!data.enable ||
((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
return;
}
data.time ??= 0;
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
data.time += delta.value;
}
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
return;
}
const offset = data.offset ? randomInRangeValue(data.offset) : minOffset, velocity = (data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor, decay = data.decay ?? identity, max = data.max, min = data.min;
if (!decrease || data.status === exports.AnimationStatus.increasing) {
data.value += velocity;
if (data.value > max) {
data.loops ??= 0;
data.loops++;
if (decrease) {
data.status = exports.AnimationStatus.decreasing;
}
else {
data.value -= max;
}
}
}
else {
data.value -= velocity;
if (data.value < min) {
data.loops ??= 0;
data.loops++;
data.status = exports.AnimationStatus.increasing;
}
}
if (data.velocity && decay !== identity) {
data.velocity *= decay;
}
data.value = clamp(data.value, min, max);
}
function updateColor(color, delta, hdr) {
if (!color) {
return;
}
const { h, s, l } = color;
updateColorValue(h, false, delta);
updateColorValue(s, true, delta);
updateColorValue(l, true, delta);
}
function alterHsl(color, type, value) {
return {
h: color.h,
s: color.s,
l: color.l + (type === exports.AlterType.darken ? -lFactor : lFactor) * value,
};
}
const tsParticles = initEngine();
const defaultHeightFactor$1 = half$2, defaultHeadWidthFactor$1 = 0.2, defaultBodyHeightFactor$1 = half$2;
function drawArrow(data) {
const { context, particle, radius } = data, width = radius * double, heightFactor = particle.heightFactor ?? defaultHeightFactor$1, headWidthFactor = particle.headWidthFactor ?? defaultHeadWidthFactor$1, bodyHeightFactor = particle.bodyHeightFactor ?? defaultBodyHeightFactor$1, height = width * heightFactor, headWidth = width * headWidthFactor, bodyHeight = height * bodyHeightFactor;
context.moveTo(-width * half$2, originPoint.y);
context.lineTo(-width * half$2, -bodyHeight * half$2);
context.lineTo(width * half$2 - headWidth, -bodyHeight * half$2);
context.lineTo(width * half$2 - headWidth, -height * half$2);
context.lineTo(width * half$2 + headWidth, originPoint.y);
context.lineTo(width * half$2 - headWidth, height * half$2);
context.lineTo(width * half$2 - headWidth, bodyHeight * half$2);
context.lineTo(-width * half$2, bodyHeight * half$2);
context.lineTo(-width * half$2, originPoint.y);
}
const defaultHeightFactor = 0.5, defaultHeadWidthFactor = 0.2, defaultBodyHeightFactor = 0.5;
class ArrowDrawer {
draw(data) {
drawArrow(data);
}
particleInit(_container, particle) {
const shapeData = particle.shapeData;
particle.heightFactor = getRangeValue(shapeData?.heightFactor ?? defaultHeightFactor);
particle.headWidthFactor = getRangeValue(shapeData?.headWidthFactor ?? defaultHeadWidthFactor);
particle.bodyHeightFactor = getRangeValue(shapeData?.bodyHeightFactor ?? defaultBodyHeightFactor);
}
}
async function loadArrowShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["arrow"], () => Promise.resolve(new ArrowDrawer()));
});
}
class BackgroundMaskCover {
color;
draw;
element;
image;
opacity = 1;
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "draw", data.draw);
loadProperty(this, "element", data.element);
loadProperty(this, "image", data.image);
loadProperty(this, "opacity", data.opacity);
}
}
class BackgroundMask {
composite = "destination-out";
cover = new BackgroundMaskCover();
enable = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "composite", data.composite);
if (data.cover !== undefined) {
const cover = data.cover, color = (isString(data.cover) ? { color: data.cover } : data.cover);
this.cover.load(cover.color !== undefined ||
cover.draw !== undefined ||
cover.element !== undefined ||
cover.image !== undefined
? cover
: { color: color });
}
loadProperty(this, "enable", data.enable);
}
}
class BackgroundMaskPlugin {
id = "background-mask";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { BackgroundMaskPluginInstance } = await Promise.resolve().then(function () { return BackgroundMaskPluginInstance$1; });
return new BackgroundMaskPluginInstance(this.#pluginManager, container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let backgroundMaskOptions = options.backgroundMask;
if (!backgroundMaskOptions?.load) {
options.backgroundMask = backgroundMaskOptions = new BackgroundMask();
}
backgroundMaskOptions.load(source?.backgroundMask);
}
needsPlugin(options) {
return !!options?.backgroundMask?.enable;
}
}
async function loadBackgroundMaskPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new BackgroundMaskPlugin(e.pluginManager));
});
}
class MovePlugin {
id = "move";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { MovePluginInstance } = await Promise.resolve().then(function () { return MovePluginInstance$1; });
return new MovePluginInstance(this.#pluginManager, container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadMovePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
const moveEngine = e, movePluginManager = moveEngine.pluginManager;
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.pathGenerators ??= new Map();
movePluginManager.addPathGenerator = (name, generator) => {
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.initializers.pathGenerators.set(name, generator);
};
movePluginManager.getPathGenerators = async (container, force = false) => {
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.pathGenerators ??= new Map();
return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
};
e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
});
}
function ensureBaseMoverLoaded(e) {
if (!e.pluginManager.addPathGenerator) {
throw new Error("tsParticles Base Mover is not loaded");
}
}
const defaultOptions$6 = {
segmentLength: 20,
branchChance: 0.2,
maxAngle: Math.PI / 3,
speedVariation: 0.3,
inertia: {
enable: false,
factor: 0.1,
},
};
class BranchesPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = {
...defaultOptions$6,
inertia: { ...defaultOptions$6.inertia },
};
}
generate(p) {
const opts = this.options;
p.branching ??= {
angle: getRandom() * doublePI,
remaining: opts.segmentLength,
baseSpeed: p.velocity.length,
speed: p.velocity.length,
};
const b = p.branching;
if (b.remaining <= 0) {
const branch = getRandom() < (opts.branchChance ?? 0), maxAngle = opts.maxAngle ?? defaultOptions$6.maxAngle, delta = (getRandom() * 2 - 1) * (branch ? maxAngle : maxAngle * 0.3);
b.angle += delta;
b.remaining = opts.segmentLength;
const speedFactor = 1 + (getRandom() * 2 - 1) * (opts.speedVariation ?? 0);
b.speed = b.baseSpeed * speedFactor;
}
b.remaining -= b.speed;
const targetX = Math.cos(b.angle) * b.speed, targetY = Math.sin(b.angle) * b.speed, inertia = opts.inertia;
if (inertia?.enable) {
const factor = inertia.factor ?? 0.1;
p.velocity.x += (targetX - p.velocity.x) * factor;
p.velocity.y += (targetY - p.velocity.y) * factor;
this.#res.x = p.velocity.x;
this.#res.y = p.velocity.y;
}
else {
p.velocity.x = 0;
p.velocity.y = 0;
this.#res.x = targetX;
this.#res.y = targetY;
}
return this.#res;
}
init() {
const source = this.#container.actualOptions.particles.move.path.options;
this.options.segmentLength = source["segmentLength"] ?? this.options.segmentLength;
this.options.branchChance = source["branchChance"] ?? this.options.branchChance;
this.options.maxAngle = source["maxAngle"] ?? this.options.maxAngle;
this.options.speedVariation = source["speedVariation"] ?? this.options.speedVariation;
const inertiaSource = source["inertia"];
if (inertiaSource) {
const inertiaOptions = { ...defaultOptions$6.inertia };
inertiaOptions.enable = inertiaSource.enable ?? inertiaOptions.enable;
inertiaOptions.factor = inertiaSource.factor ?? inertiaOptions.factor;
this.options.inertia = inertiaOptions;
}
}
reset(p) {
delete p.branching;
}
update() {
}
}
const branchingPathName = "branchesPathGenerator";
async function loadBranchesPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(branchingPathName, container => {
return Promise.resolve(new BranchesPathGenerator(container));
});
});
}
class BrownianPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = {
angleDelta: Math.PI / 12,
damping: identity$3,
};
}
generate(p) {
p.brownian ??= {
angle: getRandom() * doublePI,
speed: p.velocity.length,
};
const b = p.brownian, delta = (getRandom() * 2 - 1) * (this.options.angleDelta ?? 0);
b.angle += delta;
const damping = this.options.damping ?? identity$3;
p.velocity.x = 0;
p.velocity.y = 0;
this.#res.length = b.speed * damping;
this.#res.angle = b.angle;
return this.#res;
}
init() {
const source = this.#container.actualOptions.particles.move.path.options;
this.options.angleDelta = source["angleDelta"] ?? this.options.angleDelta;
this.options.damping = source["damping"] ?? this.options.damping;
}
reset(p) {
delete p.brownian;
}
update() {
}
}
const brownianPathName = "brownianPathGenerator";
async function loadBrownianPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(brownianPathName, container => {
return Promise.resolve(new BrownianPathGenerator(container));
});
});
}
const bubbleFactor = 3;
class BubbleDrawer {
drawAfter(data) {
const { context, radius } = data, bubbleRadius = radius / bubbleFactor;
context.beginPath();
context.arc(bubbleRadius, -bubbleRadius, bubbleRadius, defaultAngle$1, doublePI, false);
context.closePath();
context.fillStyle = "#fff9";
context.fill();
}
}
async function loadBubbleEffect(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addEffect("bubble", () => {
return Promise.resolve(new BubbleDrawer());
});
});
}
class CanvasMaskOverride {
color = true;
opacity = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "color", data.color);
loadProperty(this, "opacity", data.opacity);
}
}
const minAlpha$1 = 0;
class CanvasMaskPixels {
offset = 4;
filter = (pixel) => pixel.a > minAlpha$1;
load(data) {
if (isNull(data)) {
return;
}
if (data.filter !== undefined) {
if (isString(data.filter)) {
if (data.filter in globalThis) {
const filter = globalThis[data.filter];
if (isFunction(filter)) {
this.filter = filter;
}
}
}
else {
this.filter = data.filter;
}
}
loadProperty(this, "offset", data.offset);
}
}
class ImageMask {
src = "";
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "src", data.src);
}
}
class FontTextMask {
family = "sans-serif";
size = 100;
style = "";
variant = "";
weight = "";
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "family", data.family);
loadProperty(this, "size", data.size);
loadProperty(this, "style", data.style);
loadProperty(this, "variant", data.variant);
loadProperty(this, "weight", data.weight);
}
}
class TextMaskLine {
separator = "\n";
spacing = 10;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "separator", data.separator);
loadProperty(this, "spacing", data.spacing);
}
}
class TextMask {
color = "#000000";
fill = true;
font = new FontTextMask();
lines = new TextMaskLine();
text = "";
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "color", data.color);
loadProperty(this, "fill", data.fill);
this.font.load(data.font);
this.lines.load(data.lines);
loadProperty(this, "text", data.text);
}
}
class CanvasMask {
element;
enable = false;
image;
override = new CanvasMaskOverride();
pixels = new CanvasMaskPixels();
position;
scale = 1;
selector;
text;
constructor() {
this.position = {
x: 50,
y: 50,
};
}
load(data) {
if (isNull(data)) {
return;
}
if (data.element !== undefined && data.element instanceof HTMLCanvasElement) {
this.element = data.element;
}
loadProperty(this, "enable", data.enable);
if (data.image) {
this.image ??= new ImageMask();
this.image.load(data.image);
}
this.pixels.load(data.pixels);
if (data.position) {
this.position = {
x: data.position.x ?? this.position.x,
y: data.position.y ?? this.position.y,
};
}
this.override.load(data.override);
loadProperty(this, "scale", data.scale);
loadProperty(this, "selector", data.selector);
if (data.text) {
this.text ??= new TextMask();
this.text.load(data.text);
}
}
}
class CanvasMaskPlugin {
id = "canvas-mask";
async getPlugin(container) {
const { CanvasMaskPluginInstance } = await Promise.resolve().then(function () { return CanvasMaskPluginInstance$1; });
return new CanvasMaskPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let canvasMaskOptions = options.canvasMask;
if (canvasMaskOptions?.load === undefined) {
options.canvasMask = canvasMaskOptions = new CanvasMask();
}
canvasMaskOptions.load(source?.canvasMask);
}
needsPlugin(options) {
return options?.canvasMask?.enable ?? false;
}
}
async function loadCanvasMaskPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new CanvasMaskPlugin());
});
}
var SegmentType;
(function (SegmentType) {
SegmentType["move"] = "move";
SegmentType["line"] = "line";
SegmentType["bezier"] = "bezier";
SegmentType["quadratic"] = "quadratic";
SegmentType["arc"] = "arc";
SegmentType["ellipse"] = "ellipse";
})(SegmentType || (SegmentType = {}));
const firstIndex$5 = 0, index2 = 1, index3 = 2, index4 = 3;
function drawPath(ctx, radius, path) {
const firstSegment = path.segments[firstIndex$5];
if (!firstSegment) {
return;
}
const firstValue = firstSegment.values[firstIndex$5];
if (!firstValue) {
return;
}
ctx.moveTo(firstValue.x * radius, firstValue.y * radius);
for (const segment of path.segments) {
const value = segment.values[firstIndex$5];
if (!value) {
continue;
}
const segmentValue2 = segment.values[index2], segmentValue3 = segment.values[index3], segmentValue4 = segment.values[index4];
switch (segment.type) {
case SegmentType.move:
ctx.moveTo(value.x * radius, value.y * radius);
break;
case SegmentType.line:
ctx.lineTo(value.x * radius, value.y * radius);
break;
case SegmentType.bezier:
if (!segmentValue2 || !segmentValue3 || !segmentValue4) {
continue;
}
ctx.bezierCurveTo(segmentValue2.x * radius, segmentValue2.y * radius, segmentValue3.x * radius, segmentValue3.y * radius, segmentValue4.x * radius, segmentValue4.y * radius);
break;
case SegmentType.quadratic:
if (!segmentValue2 || !segmentValue3) {
continue;
}
ctx.quadraticCurveTo(segmentValue2.x * radius, segmentValue2.y * radius, segmentValue3.x * radius, segmentValue3.y * radius);
break;
case SegmentType.arc:
if (!segmentValue2 || !segmentValue3) {
continue;
}
ctx.arc(value.x * radius, value.y * radius, segmentValue2.x * radius, segmentValue3.x, segmentValue3.y);
break;
case SegmentType.ellipse:
if (!segmentValue2 || !segmentValue3 || !segmentValue4) {
continue;
}
ctx.ellipse(value.x * radius, value.y * radius, segmentValue2.x * radius, segmentValue2.y * radius, segmentValue3.x, segmentValue4.x, segmentValue4.y);
break;
}
}
if (!path.half) {
return;
}
const lengthOffset = 1, minLength = 0;
for (let i = path.segments.length - lengthOffset; i >= minLength; i--) {
const segment = path.segments[i];
if (!segment) {
continue;
}
const value = segment.values[firstIndex$5], index2 = 1, index3 = 2, segmentValue2 = segment.values[index2], segmentValue3 = segment.values[index3];
switch (segment.type) {
case SegmentType.line:
if (!value) {
break;
}
ctx.lineTo(value.x * -radius, value.y * radius);
break;
case SegmentType.bezier:
if (!value || !segmentValue2 || !segmentValue3) {
break;
}
ctx.bezierCurveTo(-segmentValue3.x * radius, segmentValue3.y * radius, -segmentValue2.x * radius, segmentValue2.y * radius, -value.x * radius, value.y * radius);
break;
case SegmentType.quadratic:
if (!segmentValue2 || !segmentValue3) {
break;
}
ctx.quadraticCurveTo(-segmentValue2.x * radius, segmentValue2.y * radius, -segmentValue3.x * radius, segmentValue3.y * radius);
break;
case SegmentType.arc:
case SegmentType.ellipse:
}
}
}
var SuitType;
(function (SuitType) {
SuitType["hearts"] = "hearts";
SuitType["diamonds"] = "diamonds";
SuitType["clubs"] = "clubs";
SuitType["spades"] = "spades";
})(SuitType || (SuitType = {}));
const eighth = quarter$2 * half$2, n = half$2, halfN = n * half$2, oppositeN = -n, oppositeHalfN = -halfN, eighthN = n * eighth, threeQuarterN = n * threeQuarter;
const club = {
half: true,
segments: [
{
type: SegmentType.bezier,
values: [
{ x: empty$1, y: oppositeN },
{ x: empty$1, y: oppositeN },
{ x: halfN, y: oppositeN },
{ x: halfN, y: oppositeHalfN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: oppositeHalfN },
{ x: halfN, y: oppositeHalfN },
{ x: n, y: oppositeHalfN },
{ x: n, y: empty$1 },
],
},
{
type: SegmentType.bezier,
values: [
{ x: n, y: empty$1 },
{ x: n, y: empty$1 },
{ x: n, y: halfN },
{ x: halfN, y: halfN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: halfN },
{ x: halfN, y: halfN },
{ x: eighthN, y: halfN },
{ x: eighthN, y: eighthN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: eighthN, y: eighthN },
{ x: eighthN, y: halfN },
{ x: halfN, y: n },
{ x: halfN, y: n },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: n },
{ x: halfN, y: n },
{ x: empty$1, y: n },
{ x: empty$1, y: n },
],
},
],
};
const diamond = {
half: true,
segments: [
{
type: SegmentType.bezier,
values: [
{ x: empty$1, y: n },
{ x: empty$1, y: n },
{ x: threeQuarterN, y: empty$1 },
{ x: threeQuarterN, y: empty$1 },
],
},
{
type: SegmentType.bezier,
values: [
{ x: threeQuarterN, y: empty$1 },
{ x: threeQuarterN, y: empty$1 },
{ x: empty$1, y: oppositeN },
{ x: empty$1, y: oppositeN },
],
},
],
};
const heart = {
half: true,
segments: [
{
type: SegmentType.bezier,
values: [
{ x: empty$1, y: n },
{ x: empty$1, y: n },
{ x: n, y: empty$1 },
{ x: n, y: oppositeHalfN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: n, y: oppositeHalfN },
{ x: n, y: oppositeHalfN },
{ x: n, y: oppositeN },
{ x: halfN, y: oppositeN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: oppositeN },
{ x: halfN, y: oppositeN },
{ x: empty$1, y: oppositeN },
{ x: empty$1, y: oppositeHalfN },
],
},
],
};
const spade = {
half: true,
segments: [
{
type: SegmentType.bezier,
values: [
{ x: empty$1, y: oppositeN },
{ x: empty$1, y: oppositeN },
{ x: n, y: oppositeHalfN },
{ x: n, y: empty$1 },
],
},
{
type: SegmentType.bezier,
values: [
{ x: n, y: empty$1 },
{ x: n, y: empty$1 },
{ x: n, y: halfN },
{ x: halfN, y: halfN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: halfN },
{ x: halfN, y: halfN },
{ x: eighthN, y: halfN },
{ x: eighthN, y: eighthN },
],
},
{
type: SegmentType.bezier,
values: [
{ x: eighthN, y: eighthN },
{ x: eighthN, y: halfN },
{ x: halfN, y: n },
{ x: halfN, y: n },
],
},
{
type: SegmentType.bezier,
values: [
{ x: halfN, y: n },
{ x: halfN, y: n },
{ x: empty$1, y: n },
{ x: empty$1, y: n },
],
},
],
};
const paths = {
[SuitType.hearts]: heart,
[SuitType.diamonds]: diamond,
[SuitType.clubs]: club,
[SuitType.spades]: spade,
};
class ClubDrawer {
draw(data) {
const { context, radius } = data;
drawPath(context, radius, paths.clubs);
}
}
async function loadClubsSuitShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["club", "clubs"], () => Promise.resolve(new ClubDrawer()));
});
}
class DiamondDrawer {
draw(data) {
const { context, radius } = data;
drawPath(context, radius, paths.diamonds);
}
}
async function loadDiamondsSuitShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["diamond", "diamonds"], () => Promise.resolve(new DiamondDrawer()));
});
}
let HeartDrawer$1 = class HeartDrawer {
draw(data) {
const { context, radius } = data;
drawPath(context, radius, paths.hearts);
}
};
async function loadHeartsSuitShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["heart", "hearts"], () => Promise.resolve(new HeartDrawer$1()));
});
}
class SpadeDrawer {
draw(data) {
const { context, radius } = data;
drawPath(context, radius, paths.spades);
}
}
async function loadSpadesSuitShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["spade", "spades"], () => Promise.resolve(new SpadeDrawer()));
});
}
async function loadCardSuitsShape(engine) {
engine.checkVersion("4.4.0");
await Promise.all([
loadClubsSuitShape(engine),
loadDiamondsSuitShape(engine),
loadHeartsSuitShape(engine),
loadSpadesSuitShape(engine),
]);
}
const cardWidthRatio = (double * double) / triple, cardHeightRatio = double, cornerRadiusRatio = 0.2, cornerFontRatio = 0.4, cornerSuitRatio = 0.3, centerSuitRatio = 0.7, cornerPaddingRatio = 0.2, textHorizontalOffsetRatio = 0.25, minRadius$6 = 0, minAngle$5 = 0, suitEdgeBufferFactor = 0.1, fixedCacheKey = 2, cardsCache = new Map(), redSuitColor = { r: 215, g: 20, b: 20 }, blackSuitColor = { r: 18, g: 18, b: 18 };
function drawRoundedCard(ctx, radius, cardData, hdr, flipped, canvasSettings, peakNits, mode) {
if (flipped) {
drawRoundedCardBack(ctx, radius);
}
else {
const cacheKey = getCacheKey(radius, hdr, cardData, peakNits, mode), cardWidth = radius * cardWidthRatio, cardHeight = radius * cardHeightRatio, halfWidth = cardWidth * half$2, halfHeight = cardHeight * half$2;
let cachedData = cardsCache.get(cacheKey);
if (!cachedData) {
const cacheCanvas = new OffscreenCanvas(cardWidth, cardHeight), cacheCtx = cacheCanvas.getContext("2d", canvasSettings);
if (cacheCtx) {
cacheCtx.translate(halfWidth, halfHeight);
drawRoundedCardFront(cacheCtx, radius, cardData, hdr, peakNits, mode);
cachedData = cacheCanvas instanceof HTMLCanvasElement ? cacheCanvas : cacheCanvas.transferToImageBitmap();
cardsCache.set(cacheKey, cachedData);
}
}
if (cachedData) {
ctx.drawImage(cachedData, -halfWidth, -halfHeight, cardWidth, cardHeight);
}
else {
drawRoundedCardFront(ctx, radius, cardData, hdr, peakNits, mode);
}
}
}
function getCacheKey(radius, hdr, cardData, peakNits, mode) {
return `${radius.toFixed(fixedCacheKey)}-${hdr ? "hdr" : "sdr"}-${peakNits !== undefined ? peakNits.toFixed(fixedCacheKey) : "default"}-${mode ?? "default"}-${cardData.suit}-${cardData.value}`;
}
function drawRoundedCardBack(ctx, radius) {
drawCardBody(ctx, radius);
}
function drawRoundedCardFront(ctx, radius, cardData, hdr, peakNits, mode) {
const { suit, value } = cardData, cardWidth = radius * cardWidthRatio, cardHeight = radius * cardHeightRatio, halfWidth = cardWidth * half$2, halfHeight = cardHeight * half$2, cornerPadding = radius * cornerPaddingRatio, cornerFontSize = radius * cornerFontRatio, cornerSuitSize = radius * cornerSuitRatio, centerSuitSize = radius * centerSuitRatio, textOffset = radius * textHorizontalOffsetRatio, suitEdgeBuffer = radius * suitEdgeBufferFactor, isRed = suit === SuitType.hearts || suit === SuitType.diamonds, color = isRed
? getStyleFromRgb(redSuitColor, hdr, undefined, peakNits, mode)
: getStyleFromRgb(blackSuitColor, hdr, undefined, peakNits, mode);
ctx.save();
drawCardBody(ctx, radius);
ctx.fillStyle = getStyleFromRgb({ r: 255, g: 255, b: 255 }, hdr, undefined, peakNits, mode);
ctx.fill();
ctx.strokeStyle = getStyleFromRgb({ r: 0, g: 0, b: 0 }, hdr, undefined, peakNits, mode);
ctx.stroke();
ctx.fillStyle = color;
ctx.font = `bold ${cornerFontSize.toString()}px Arial, serif`;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
const topLeftX = -halfWidth + cornerPadding + suitEdgeBuffer, topLeftY = -halfHeight + cornerPadding + cornerFontSize * half$2;
ctx.save();
ctx.translate(topLeftX, topLeftY);
ctx.beginPath();
drawPath(ctx, cornerSuitSize, paths[suit]);
ctx.fill();
ctx.restore();
ctx.fillText(value, topLeftX + textOffset, topLeftY);
ctx.save();
ctx.translate(halfWidth - cornerPadding - suitEdgeBuffer, halfHeight - cornerPadding - cornerFontSize * half$2);
ctx.rotate(Math.PI);
ctx.save();
ctx.beginPath();
drawPath(ctx, cornerSuitSize, paths[suit]);
ctx.fill();
ctx.restore();
ctx.fillText(value, originPoint.x + textOffset, originPoint.y);
ctx.restore();
ctx.save();
ctx.beginPath();
drawPath(ctx, centerSuitSize, paths[suit]);
ctx.fillStyle = color;
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.arc(originPoint.x, originPoint.y, minRadius$6, minAngle$5, doublePI, false);
ctx.closePath();
ctx.restore();
}
function drawCardBody(ctx, radius) {
const cardWidth = radius * cardWidthRatio, cardHeight = radius * cardHeightRatio, halfWidth = cardWidth * half$2, halfHeight = cardHeight * half$2, cornerRadius = radius * cornerRadiusRatio;
ctx.beginPath();
ctx.moveTo(-halfWidth + cornerRadius, -halfHeight);
ctx.lineTo(halfWidth - cornerRadius, -halfHeight);
ctx.quadraticCurveTo(halfWidth, -halfHeight, halfWidth, -halfHeight + cornerRadius);
ctx.lineTo(halfWidth, halfHeight - cornerRadius);
ctx.quadraticCurveTo(halfWidth, halfHeight, halfWidth - cornerRadius, halfHeight);
ctx.lineTo(-halfWidth + cornerRadius, halfHeight);
ctx.quadraticCurveTo(-halfWidth, halfHeight, -halfWidth, halfHeight - cornerRadius);
ctx.lineTo(-halfWidth, -halfHeight + cornerRadius);
ctx.quadraticCurveTo(-halfWidth, -halfHeight, -halfWidth + cornerRadius, -halfHeight);
ctx.closePath();
}
class CardDrawer {
#container;
constructor(container) {
this.#container = container;
}
draw(data) {
const { context, particle, opacity, radius } = data;
if (!particle.cardData) {
return;
}
const defaultOpacity = context.globalAlpha, container = this.#container;
context.globalAlpha = opacity;
drawRoundedCard(context, radius, particle.cardData, container.hdr, particle.isShowingBack(), container.canvas.render.settings, container.peakNits, container.hdrMode);
context.globalAlpha = defaultOpacity;
}
particleInit(_container, particle) {
const shape = particle.shapeData;
if (!shape) {
return;
}
particle.cardData = deepExtend({}, shape);
}
}
async function loadFullCardsShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["card"], container => Promise.resolve(new CardDrawer(container)));
});
}
async function loadCardsShape(engine) {
engine.checkVersion("4.4.0");
await Promise.all([
loadFullCardsShape(engine),
loadCardSuitsShape(engine),
]);
}
const minAngle$4 = 0, taperFactor = 0.005;
function drawCogHole(data) {
const { context, particle, radius } = data;
if (particle.cogHoleRadius === undefined ||
particle.cogInnerRadius === undefined ||
particle.cogInnerTaper === undefined ||
particle.cogNotches === undefined ||
particle.cogOuterTaper === undefined) {
return;
}
const holeRadius = (radius * particle.cogHoleRadius) / percentDenominator;
context.globalCompositeOperation = "destination-out";
context.beginPath();
context.moveTo(holeRadius, originPoint.y);
context.arc(originPoint.x, originPoint.y, holeRadius, minAngle$4, doublePI);
context.closePath();
context.fill();
context.globalCompositeOperation = "source-over";
}
function drawCog(data) {
const { context, particle, radius } = data;
if (particle.cogHoleRadius === undefined ||
particle.cogInnerRadius === undefined ||
particle.cogInnerTaper === undefined ||
particle.cogNotches === undefined ||
particle.cogOuterTaper === undefined) {
return;
}
const angle = doublePI / (particle.cogNotches * double), taperAI = angle * particle.cogInnerTaper * taperFactor, taperAO = angle * particle.cogOuterTaper * taperFactor, innerRadius = (radius * particle.cogInnerRadius) / percentDenominator;
let a = angle, toggle = false;
context.moveTo(radius * Math.cos(taperAO), radius * Math.sin(taperAO));
for (; a <= doublePI; a += angle) {
if (toggle) {
context.lineTo(innerRadius * Math.cos(a - taperAI), innerRadius * Math.sin(a - taperAI));
context.lineTo(radius * Math.cos(a + taperAO), radius * Math.sin(a + taperAO));
}
else {
context.lineTo(radius * Math.cos(a - taperAO), radius * Math.sin(a - taperAO));
context.lineTo(innerRadius * Math.cos(a + taperAI), innerRadius * Math.sin(a + taperAI));
}
toggle = !toggle;
}
}
const defaultHoleRadius = 44, defaultInnerRadius$1 = 72, defaultInnerTaper = 35, defaultNotches = 7, defaultOuterTaper = 50;
class CogDrawer {
afterDraw(data) {
drawCogHole(data);
}
draw(data) {
drawCog(data);
}
particleInit(_container, particle) {
const shapeData = particle.shapeData;
particle.cogHoleRadius = getRangeValue(shapeData?.holeRadius ?? defaultHoleRadius);
particle.cogInnerRadius = getRangeValue(shapeData?.innerRadius ?? defaultInnerRadius$1);
particle.cogInnerTaper = getRangeValue(shapeData?.innerTaper ?? defaultInnerTaper);
particle.cogNotches = getRangeValue(shapeData?.notches ?? defaultNotches);
particle.cogOuterTaper = getRangeValue(shapeData?.outerTaper ?? defaultOuterTaper);
}
}
async function loadCogShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["cog"], () => Promise.resolve(new CogDrawer()));
});
}
function shuffleSeed(seed) {
const newSeed = new Uint32Array(1);
newSeed[0] = seed[0] * 1664525 + 1013904223;
return newSeed;
}
const half$1 = 0.5;
class SimplexNoise2D {
#NORM_2D;
#SQUISH_2D;
#STRETCH_2D;
#base2D;
#gradients2D;
#lookup;
#lookupPairs2D;
#p2D;
#perm;
#perm2D;
constructor() {
this.#NORM_2D = 1 / 47;
this.#SQUISH_2D = (Math.sqrt(2 + 1) - 1) * half$1;
this.#STRETCH_2D = (1 / Math.sqrt(2 + 1) - 1) * half$1;
this.#base2D = [
[1, 1, 0, 1, 0, 1, 0, 0, 0],
[1, 1, 0, 1, 0, 1, 2, 1, 1],
];
this.#gradients2D = [
5,
2,
2,
5,
-5,
2,
-2,
5,
5,
-2,
2,
-5,
-5,
-2,
-2,
-5,
];
this.#lookup = [];
this.#lookupPairs2D = [
0,
1,
1,
0,
4,
1,
17,
0,
20,
2,
21,
2,
22,
5,
23,
5,
26,
4,
39,
3,
42,
4,
43,
3,
];
this.#p2D = [
0,
0,
1,
-1,
0,
0,
-1,
1,
0,
2,
1,
1,
1,
2,
2,
0,
1,
2,
0,
2,
1,
0,
0,
0,
];
this.#perm = new Uint8Array(256);
this.#perm2D = new Uint8Array(256);
}
noise(x, y) {
const gradients2D = this.#gradients2D, NORM_2D = this.#NORM_2D, SQUISH_2D = this.#SQUISH_2D, STRETCH_2D = this.#STRETCH_2D, lookup = this.#lookup, perm = this.#perm, perm2D = this.#perm2D, stretchOffset = (x + y) * STRETCH_2D, xs = x + stretchOffset, ys = y + stretchOffset, xsb = Math.floor(xs), ysb = Math.floor(ys), squishOffset = (xsb + ysb) * SQUISH_2D, dx0 = x - (xsb + squishOffset), dy0 = y - (ysb + squishOffset), xins = xs - xsb, yins = ys - ysb, inSum = xins + yins, hash = (xins - yins + 1) | (inSum << 1) | ((inSum + yins) << 2) | ((inSum + xins) << 4);
let value = 0;
for (let c = lookup[hash]; c !== undefined; c = c.next) {
const dx = dx0 + c.dx, dy = dy0 + c.dy, attn = 2 - dx * dx - dy * dy;
if (attn <= 0) {
continue;
}
const px = xsb + c.xsb, py = ysb + c.ysb, indexPartA = perm[px & 0xff], index = perm2D[(indexPartA + py) & 0xff], valuePart = gradients2D[index] * dx + gradients2D[index + 1] * dy;
value += attn * attn * attn * attn * valuePart;
}
return value * NORM_2D;
}
seed(clientSeed) {
const p2D = this.#p2D, base2D = this.#base2D, lookupPairs2D = this.#lookupPairs2D, contributions = [];
for (let i = 0; i < p2D.length; i += 4) {
const baseSet = base2D[p2D[i]];
let previous = null, current = null;
for (let k = 0; k < baseSet.length; k += 3) {
current = this.#contribution2D(baseSet[k], baseSet[k + 1], baseSet[k + 2]);
if (previous === null) {
contributions[i / 4] = current;
}
else {
previous.next = current;
}
previous = current;
}
if (current) {
current.next = this.#contribution2D(p2D[i + 1], p2D[i + 2], p2D[i + 3]);
}
}
this.#lookup = [];
for (let i = 0; i < lookupPairs2D.length; i += 2) {
this.#lookup[lookupPairs2D[i]] = contributions[lookupPairs2D[i + 1]];
}
this.#perm = new Uint8Array(256);
this.#perm2D = new Uint8Array(256);
const source = new Uint8Array(256);
for (let i = 0; i < 256; i++) {
source[i] = i;
}
let seed = new Uint32Array(1);
seed[0] = clientSeed;
seed = shuffleSeed(shuffleSeed(shuffleSeed(seed)));
for (let i = 255; i >= 0; i--) {
seed = shuffleSeed(seed);
const r = new Uint32Array(1);
r[0] = (seed[0] + 31) % (i + 1);
if (r[0] < 0) {
r[0] += i + 1;
}
this.#perm[i] = source[r[0]];
this.#perm2D[i] = this.#perm[i] & 0x0e;
source[r[0]] = source[i];
}
}
#contribution2D(multiplier, xsb, ysb) {
const SQUISH_2D = this.#SQUISH_2D;
return {
dx: -xsb - multiplier * SQUISH_2D,
dy: -ysb - multiplier * SQUISH_2D,
xsb,
ysb,
};
}
}
const third = 1 / 3;
class SimplexNoise3D {
#NORM_3D;
#SQUISH_3D;
#STRETCH_3D;
#base3D;
#gradients3D;
#lookup;
#lookupPairs3D;
#p3D;
#perm;
#perm3D;
constructor() {
this.#NORM_3D = 1 / 103;
this.#SQUISH_3D = (Math.sqrt(3 + 1) - 1) * third;
this.#STRETCH_3D = (1 / Math.sqrt(3 + 1) - 1) * third;
this.#base3D = [
[0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1],
[2, 1, 1, 0, 2, 1, 0, 1, 2, 0, 1, 1, 3, 1, 1, 1],
[1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 2, 1, 1, 0, 2, 1, 0, 1, 2, 0, 1, 1],
];
this.#gradients3D = [
-11,
4,
4,
-4,
11,
4,
-4,
4,
11,
11,
4,
4,
4,
11,
4,
4,
4,
11,
-11,
-4,
4,
-4,
-11,
4,
-4,
-4,
11,
11,
-4,
4,
4,
-11,
4,
4,
-4,
11,
-11,
4,
-4,
-4,
11,
-4,
-4,
4,
-11,
11,
4,
-4,
4,
11,
-4,
4,
4,
-11,
-11,
-4,
-4,
-4,
-11,
-4,
-4,
-4,
-11,
11,
-4,
-4,
4,
-11,
-4,
4,
-4,
-11,
];
this.#lookup = [];
this.#lookupPairs3D = [
0,
2,
1,
1,
2,
2,
5,
1,
6,
0,
7,
0,
32,
2,
34,
2,
129,
1,
133,
1,
160,
5,
161,
5,
518,
0,
519,
0,
546,
4,
550,
4,
645,
3,
647,
3,
672,
5,
673,
5,
674,
4,
677,
3,
678,
4,
679,
3,
680,
13,
681,
13,
682,
12,
685,
14,
686,
12,
687,
14,
712,
20,
714,
18,
809,
21,
813,
23,
840,
20,
841,
21,
1198,
19,
1199,
22,
1226,
18,
1230,
19,
1325,
23,
1327,
22,
1352,
15,
1353,
17,
1354,
15,
1357,
17,
1358,
16,
1359,
16,
1360,
11,
1361,
10,
1362,
11,
1365,
10,
1366,
9,
1367,
9,
1392,
11,
1394,
11,
1489,
10,
1493,
10,
1520,
8,
1521,
8,
1878,
9,
1879,
9,
1906,
7,
1910,
7,
2005,
6,
2007,
6,
2032,
8,
2033,
8,
2034,
7,
2037,
6,
2038,
7,
2039,
6,
];
this.#p3D = [
0,
0,
1,
-1,
0,
0,
1,
0,
-1,
0,
0,
-1,
1,
0,
0,
0,
1,
-1,
0,
0,
-1,
0,
1,
0,
0,
-1,
1,
0,
2,
1,
1,
0,
1,
1,
1,
-1,
0,
2,
1,
0,
1,
1,
1,
-1,
1,
0,
2,
0,
1,
1,
1,
-1,
1,
1,
1,
3,
2,
1,
0,
3,
1,
2,
0,
1,
3,
2,
0,
1,
3,
1,
0,
2,
1,
3,
0,
2,
1,
3,
0,
1,
2,
1,
1,
1,
0,
0,
2,
2,
0,
0,
1,
1,
0,
1,
0,
2,
0,
2,
0,
1,
1,
0,
0,
1,
2,
0,
0,
2,
2,
0,
0,
0,
0,
1,
1,
-1,
1,
2,
0,
0,
0,
0,
1,
-1,
1,
1,
2,
0,
0,
0,
0,
1,
1,
1,
-1,
2,
3,
1,
1,
1,
2,
0,
0,
2,
2,
3,
1,
1,
1,
2,
2,
0,
0,
2,
3,
1,
1,
1,
2,
0,
2,
0,
2,
1,
1,
-1,
1,
2,
0,
0,
2,
2,
1,
1,
-1,
1,
2,
2,
0,
0,
2,
1,
-1,
1,
1,
2,
0,
0,
2,
2,
1,
-1,
1,
1,
2,
0,
2,
0,
2,
1,
1,
1,
-1,
2,
2,
0,
0,
2,
1,
1,
1,
-1,
2,
0,
2,
0,
];
this.#perm = new Uint8Array(256);
this.#perm3D = new Uint8Array(256);
}
noise(x, y, z) {
const STRETCH_3D = this.#STRETCH_3D, NORM_3D = this.#NORM_3D, SQUISH_3D = this.#SQUISH_3D, lookup = this.#lookup, perm = this.#perm, perm3D = this.#perm3D, gradients3D = this.#gradients3D, stretchOffset = (x + y + z) * STRETCH_3D, xs = x + stretchOffset, ys = y + stretchOffset, zs = z + stretchOffset, xsb = Math.floor(xs), ysb = Math.floor(ys), zsb = Math.floor(zs), squishOffset = (xsb + ysb + zsb) * SQUISH_3D, dx0 = x - (xsb + squishOffset), dy0 = y - (ysb + squishOffset), dz0 = z - (zsb + squishOffset), xins = xs - xsb, yins = ys - ysb, zins = zs - zsb, inSum = xins + yins + zins, hash = (yins - zins + 1) |
((xins - yins + 1) << 1) |
((xins - zins + 1) << 2) |
(inSum << 3) |
((inSum + zins) << 5) |
((inSum + yins) << 7) |
((inSum + xins) << 9);
let value = 0;
for (let c = lookup[hash]; c !== undefined; c = c.next) {
const dx = dx0 + c.dx, dy = dy0 + c.dy, dz = dz0 + c.dz, attn = 2 - dx * dx - dy * dy - dz * dz;
if (attn > 0) {
const px = xsb + c.xsb, py = ysb + c.ysb, pz = zsb + c.zsb, indexPartA = perm[px & 0xff], indexPartB = perm[(indexPartA + py) & 0xff], index = perm3D[(indexPartB + pz) & 0xff], valuePart = gradients3D[index] * dx + gradients3D[index + 1] * dy + gradients3D[index + 2] * dz;
value += attn * attn * attn * attn * valuePart;
}
}
return value * NORM_3D;
}
seed(clientSeed) {
const base3D = this.#base3D, lookupPairs3D = this.#lookupPairs3D, p3D = this.#p3D, contributions = [];
for (let i = 0; i < p3D.length; i += 9) {
const baseSet = base3D[p3D[i]];
let previous = null, current = null;
for (let k = 0; k < baseSet.length; k += 4) {
current = this.#contribution3D(baseSet[k], baseSet[k + 1], baseSet[k + 2], baseSet[k + 3]);
if (previous === null) {
contributions[i / 9] = current;
}
else {
previous.next = current;
}
previous = current;
}
if (current) {
current.next = this.#contribution3D(p3D[i + 1], p3D[i + 2], p3D[i + 3], p3D[i + 4]);
current.next.next = this.#contribution3D(p3D[i + 5], p3D[i + 6], p3D[i + 7], p3D[i + 8]);
}
}
this.#lookup = [];
for (let i = 0; i < lookupPairs3D.length; i += 2) {
this.#lookup[lookupPairs3D[i]] = contributions[lookupPairs3D[i + 1]];
}
this.#perm = new Uint8Array(256);
this.#perm3D = new Uint8Array(256);
const source = new Uint8Array(256);
for (let i = 0; i < 256; i++) {
source[i] = i;
}
let seed = new Uint32Array(1);
seed[0] = clientSeed;
seed = shuffleSeed(shuffleSeed(shuffleSeed(seed)));
for (let i = 255; i >= 0; i--) {
seed = shuffleSeed(seed);
const r = new Uint32Array(1);
r[0] = (seed[0] + 31) % (i + 1);
if (r[0] < 0) {
r[0] += i + 1;
}
this.#perm[i] = source[r[0]];
this.#perm3D[i] = (this.#perm[i] % 24) * 3;
source[r[0]] = source[i];
}
}
#contribution3D(multiplier, xsb, ysb, zsb) {
const SQUISH_3D = this.#SQUISH_3D;
return {
dx: -xsb - multiplier * SQUISH_3D,
dy: -ysb - multiplier * SQUISH_3D,
dz: -zsb - multiplier * SQUISH_3D,
xsb,
ysb,
zsb,
};
}
}
const quarter$1 = 0.25;
class SimplexNoise4D {
#NORM_4D;
#SQUISH_4D;
#STRETCH_4D;
#base4D;
#gradients4D;
#lookup;
#lookupPairs4D;
#p4D;
#perm;
#perm4D;
constructor() {
this.#NORM_4D = 1 / 30;
this.#SQUISH_4D = (Math.sqrt(4 + 1) - 1) * quarter$1;
this.#STRETCH_4D = (1 / Math.sqrt(4 + 1) - 1) * quarter$1;
this.#lookup = [];
this.#perm = new Uint8Array(0);
this.#perm4D = new Uint8Array(0);
this.#base4D = [
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[3, 1, 1, 1, 0, 3, 1, 1, 0, 1, 3, 1, 0, 1, 1, 3, 0, 1, 1, 1, 4, 1, 1, 1, 1],
[
1,
1,
0,
0,
0,
1,
0,
1,
0,
0,
1,
0,
0,
1,
0,
1,
0,
0,
0,
1,
2,
1,
1,
0,
0,
2,
1,
0,
1,
0,
2,
1,
0,
0,
1,
2,
0,
1,
1,
0,
2,
0,
1,
0,
1,
2,
0,
0,
1,
1,
],
[
3,
1,
1,
1,
0,
3,
1,
1,
0,
1,
3,
1,
0,
1,
1,
3,
0,
1,
1,
1,
2,
1,
1,
0,
0,
2,
1,
0,
1,
0,
2,
1,
0,
0,
1,
2,
0,
1,
1,
0,
2,
0,
1,
0,
1,
2,
0,
0,
1,
1,
],
];
this.#gradients4D = [
3,
1,
1,
1,
1,
3,
1,
1,
1,
1,
3,
1,
1,
1,
1,
3,
-3,
1,
1,
1,
-1,
3,
1,
1,
-1,
1,
3,
1,
-1,
1,
1,
3,
3,
-1,
1,
1,
1,
-3,
1,
1,
1,
-1,
3,
1,
1,
-1,
1,
3,
-3,
-1,
1,
1,
-1,
-3,
1,
1,
-1,
-1,
3,
1,
-1,
-1,
1,
3,
3,
1,
-1,
1,
1,
3,
-1,
1,
1,
1,
-3,
1,
1,
1,
-1,
3,
-3,
1,
-1,
1,
-1,
3,
-1,
1,
-1,
1,
-3,
1,
-1,
1,
-1,
3,
3,
-1,
-1,
1,
1,
-3,
-1,
1,
1,
-1,
-3,
1,
1,
-1,
-1,
3,
-3,
-1,
-1,
1,
-1,
-3,
-1,
1,
-1,
-1,
-3,
1,
-1,
-1,
-1,
3,
3,
1,
1,
-1,
1,
3,
1,
-1,
1,
1,
3,
-1,
1,
1,
1,
-3,
-3,
1,
1,
-1,
-1,
3,
1,
-1,
-1,
1,
3,
-1,
-1,
1,
1,
-3,
3,
-1,
1,
-1,
1,
-3,
1,
-1,
1,
-1,
3,
-1,
1,
-1,
1,
-3,
-3,
-1,
1,
-1,
-1,
-3,
1,
-1,
-1,
-1,
3,
-1,
-1,
-1,
1,
-3,
3,
1,
-1,
-1,
1,
3,
-1,
-1,
1,
1,
-3,
-1,
1,
1,
-1,
-3,
-3,
1,
-1,
-1,
-1,
3,
-1,
-1,
-1,
1,
-3,
-1,
-1,
1,
-1,
-3,
3,
-1,
-1,
-1,
1,
-3,
-1,
-1,
1,
-1,
-3,
-1,
1,
-1,
-1,
-3,
-3,
-1,
-1,
-1,
-1,
-3,
-1,
-1,
-1,
-1,
-3,
-1,
-1,
-1,
-1,
-3,
];
this.#lookupPairs4D = [
0,
3,
1,
2,
2,
3,
5,
2,
6,
1,
7,
1,
8,
3,
9,
2,
10,
3,
13,
2,
16,
3,
18,
3,
22,
1,
23,
1,
24,
3,
26,
3,
33,
2,
37,
2,
38,
1,
39,
1,
41,
2,
45,
2,
54,
1,
55,
1,
56,
0,
57,
0,
58,
0,
59,
0,
60,
0,
61,
0,
62,
0,
63,
0,
256,
3,
258,
3,
264,
3,
266,
3,
272,
3,
274,
3,
280,
3,
282,
3,
2049,
2,
2053,
2,
2057,
2,
2061,
2,
2081,
2,
2085,
2,
2089,
2,
2093,
2,
2304,
9,
2305,
9,
2312,
9,
2313,
9,
16390,
1,
16391,
1,
16406,
1,
16407,
1,
16422,
1,
16423,
1,
16438,
1,
16439,
1,
16642,
8,
16646,
8,
16658,
8,
16662,
8,
18437,
6,
18439,
6,
18469,
6,
18471,
6,
18688,
9,
18689,
9,
18690,
8,
18693,
6,
18694,
8,
18695,
6,
18696,
9,
18697,
9,
18706,
8,
18710,
8,
18725,
6,
18727,
6,
131128,
0,
131129,
0,
131130,
0,
131131,
0,
131132,
0,
131133,
0,
131134,
0,
131135,
0,
131352,
7,
131354,
7,
131384,
7,
131386,
7,
133161,
5,
133165,
5,
133177,
5,
133181,
5,
133376,
9,
133377,
9,
133384,
9,
133385,
9,
133400,
7,
133402,
7,
133417,
5,
133421,
5,
133432,
7,
133433,
5,
133434,
7,
133437,
5,
147510,
4,
147511,
4,
147518,
4,
147519,
4,
147714,
8,
147718,
8,
147730,
8,
147734,
8,
147736,
7,
147738,
7,
147766,
4,
147767,
4,
147768,
7,
147770,
7,
147774,
4,
147775,
4,
149509,
6,
149511,
6,
149541,
6,
149543,
6,
149545,
5,
149549,
5,
149558,
4,
149559,
4,
149561,
5,
149565,
5,
149566,
4,
149567,
4,
149760,
9,
149761,
9,
149762,
8,
149765,
6,
149766,
8,
149767,
6,
149768,
9,
149769,
9,
149778,
8,
149782,
8,
149784,
7,
149786,
7,
149797,
6,
149799,
6,
149801,
5,
149805,
5,
149814,
4,
149815,
4,
149816,
7,
149817,
5,
149818,
7,
149821,
5,
149822,
4,
149823,
4,
149824,
37,
149825,
37,
149826,
36,
149829,
34,
149830,
36,
149831,
34,
149832,
37,
149833,
37,
149842,
36,
149846,
36,
149848,
35,
149850,
35,
149861,
34,
149863,
34,
149865,
33,
149869,
33,
149878,
32,
149879,
32,
149880,
35,
149881,
33,
149882,
35,
149885,
33,
149886,
32,
149887,
32,
150080,
49,
150082,
48,
150088,
49,
150098,
48,
150104,
47,
150106,
47,
151873,
46,
151877,
45,
151881,
46,
151909,
45,
151913,
44,
151917,
44,
152128,
49,
152129,
46,
152136,
49,
152137,
46,
166214,
43,
166215,
42,
166230,
43,
166247,
42,
166262,
41,
166263,
41,
166466,
48,
166470,
43,
166482,
48,
166486,
43,
168261,
45,
168263,
42,
168293,
45,
168295,
42,
168512,
31,
168513,
28,
168514,
31,
168517,
28,
168518,
25,
168519,
25,
280952,
40,
280953,
39,
280954,
40,
280957,
39,
280958,
38,
280959,
38,
281176,
47,
281178,
47,
281208,
40,
281210,
40,
282985,
44,
282989,
44,
283001,
39,
283005,
39,
283208,
30,
283209,
27,
283224,
30,
283241,
27,
283256,
22,
283257,
22,
297334,
41,
297335,
41,
297342,
38,
297343,
38,
297554,
29,
297558,
24,
297562,
29,
297590,
24,
297594,
21,
297598,
21,
299365,
26,
299367,
23,
299373,
26,
299383,
23,
299389,
20,
299391,
20,
299584,
31,
299585,
28,
299586,
31,
299589,
28,
299590,
25,
299591,
25,
299592,
30,
299593,
27,
299602,
29,
299606,
24,
299608,
30,
299610,
29,
299621,
26,
299623,
23,
299625,
27,
299629,
26,
299638,
24,
299639,
23,
299640,
22,
299641,
22,
299642,
21,
299645,
20,
299646,
21,
299647,
20,
299648,
61,
299649,
60,
299650,
61,
299653,
60,
299654,
59,
299655,
59,
299656,
58,
299657,
57,
299666,
55,
299670,
54,
299672,
58,
299674,
55,
299685,
52,
299687,
51,
299689,
57,
299693,
52,
299702,
54,
299703,
51,
299704,
56,
299705,
56,
299706,
53,
299709,
50,
299710,
53,
299711,
50,
299904,
61,
299906,
61,
299912,
58,
299922,
55,
299928,
58,
299930,
55,
301697,
60,
301701,
60,
301705,
57,
301733,
52,
301737,
57,
301741,
52,
301952,
79,
301953,
79,
301960,
76,
301961,
76,
316038,
59,
316039,
59,
316054,
54,
316071,
51,
316086,
54,
316087,
51,
316290,
78,
316294,
78,
316306,
73,
316310,
73,
318085,
77,
318087,
77,
318117,
70,
318119,
70,
318336,
79,
318337,
79,
318338,
78,
318341,
77,
318342,
78,
318343,
77,
430776,
56,
430777,
56,
430778,
53,
430781,
50,
430782,
53,
430783,
50,
431000,
75,
431002,
72,
431032,
75,
431034,
72,
432809,
74,
432813,
69,
432825,
74,
432829,
69,
433032,
76,
433033,
76,
433048,
75,
433065,
74,
433080,
75,
433081,
74,
447158,
71,
447159,
68,
447166,
71,
447167,
68,
447378,
73,
447382,
73,
447386,
72,
447414,
71,
447418,
72,
447422,
71,
449189,
70,
449191,
70,
449197,
69,
449207,
68,
449213,
69,
449215,
68,
449408,
67,
449409,
67,
449410,
66,
449413,
64,
449414,
66,
449415,
64,
449416,
67,
449417,
67,
449426,
66,
449430,
66,
449432,
65,
449434,
65,
449445,
64,
449447,
64,
449449,
63,
449453,
63,
449462,
62,
449463,
62,
449464,
65,
449465,
63,
449466,
65,
449469,
63,
449470,
62,
449471,
62,
449472,
19,
449473,
19,
449474,
18,
449477,
16,
449478,
18,
449479,
16,
449480,
19,
449481,
19,
449490,
18,
449494,
18,
449496,
17,
449498,
17,
449509,
16,
449511,
16,
449513,
15,
449517,
15,
449526,
14,
449527,
14,
449528,
17,
449529,
15,
449530,
17,
449533,
15,
449534,
14,
449535,
14,
449728,
19,
449729,
19,
449730,
18,
449734,
18,
449736,
19,
449737,
19,
449746,
18,
449750,
18,
449752,
17,
449754,
17,
449784,
17,
449786,
17,
451520,
19,
451521,
19,
451525,
16,
451527,
16,
451528,
19,
451529,
19,
451557,
16,
451559,
16,
451561,
15,
451565,
15,
451577,
15,
451581,
15,
451776,
19,
451777,
19,
451784,
19,
451785,
19,
465858,
18,
465861,
16,
465862,
18,
465863,
16,
465874,
18,
465878,
18,
465893,
16,
465895,
16,
465910,
14,
465911,
14,
465918,
14,
465919,
14,
466114,
18,
466118,
18,
466130,
18,
466134,
18,
467909,
16,
467911,
16,
467941,
16,
467943,
16,
468160,
13,
468161,
13,
468162,
13,
468163,
13,
468164,
13,
468165,
13,
468166,
13,
468167,
13,
580568,
17,
580570,
17,
580585,
15,
580589,
15,
580598,
14,
580599,
14,
580600,
17,
580601,
15,
580602,
17,
580605,
15,
580606,
14,
580607,
14,
580824,
17,
580826,
17,
580856,
17,
580858,
17,
582633,
15,
582637,
15,
582649,
15,
582653,
15,
582856,
12,
582857,
12,
582872,
12,
582873,
12,
582888,
12,
582889,
12,
582904,
12,
582905,
12,
596982,
14,
596983,
14,
596990,
14,
596991,
14,
597202,
11,
597206,
11,
597210,
11,
597214,
11,
597234,
11,
597238,
11,
597242,
11,
597246,
11,
599013,
10,
599015,
10,
599021,
10,
599023,
10,
599029,
10,
599031,
10,
599037,
10,
599039,
10,
599232,
13,
599233,
13,
599234,
13,
599235,
13,
599236,
13,
599237,
13,
599238,
13,
599239,
13,
599240,
12,
599241,
12,
599250,
11,
599254,
11,
599256,
12,
599257,
12,
599258,
11,
599262,
11,
599269,
10,
599271,
10,
599272,
12,
599273,
12,
599277,
10,
599279,
10,
599282,
11,
599285,
10,
599286,
11,
599287,
10,
599288,
12,
599289,
12,
599290,
11,
599293,
10,
599294,
11,
599295,
10,
];
this.#p4D = [
0,
0,
1,
-1,
0,
0,
0,
1,
0,
-1,
0,
0,
1,
0,
0,
-1,
0,
0,
-1,
1,
0,
0,
0,
0,
1,
-1,
0,
0,
0,
1,
0,
-1,
0,
0,
-1,
0,
1,
0,
0,
0,
-1,
1,
0,
0,
0,
0,
1,
-1,
0,
0,
-1,
0,
0,
1,
0,
0,
-1,
0,
1,
0,
0,
0,
-1,
1,
0,
2,
1,
1,
0,
0,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
0,
2,
1,
0,
1,
0,
1,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
0,
2,
0,
1,
1,
0,
1,
-1,
1,
1,
0,
1,
0,
1,
1,
-1,
0,
2,
1,
0,
0,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
0,
2,
0,
1,
0,
1,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
1,
0,
2,
0,
0,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
1,
1,
4,
2,
1,
1,
0,
4,
1,
2,
1,
0,
4,
1,
1,
2,
0,
1,
4,
2,
1,
0,
1,
4,
1,
2,
0,
1,
4,
1,
1,
0,
2,
1,
4,
2,
0,
1,
1,
4,
1,
0,
2,
1,
4,
1,
0,
1,
2,
1,
4,
0,
2,
1,
1,
4,
0,
1,
2,
1,
4,
0,
1,
1,
2,
1,
2,
1,
1,
0,
0,
3,
2,
1,
0,
0,
3,
1,
2,
0,
0,
1,
2,
1,
0,
1,
0,
3,
2,
0,
1,
0,
3,
1,
0,
2,
0,
1,
2,
0,
1,
1,
0,
3,
0,
2,
1,
0,
3,
0,
1,
2,
0,
1,
2,
1,
0,
0,
1,
3,
2,
0,
0,
1,
3,
1,
0,
0,
2,
1,
2,
0,
1,
0,
1,
3,
0,
2,
0,
1,
3,
0,
1,
0,
2,
1,
2,
0,
0,
1,
1,
3,
0,
0,
2,
1,
3,
0,
0,
1,
2,
2,
3,
1,
1,
1,
0,
2,
1,
1,
1,
-1,
2,
2,
0,
0,
0,
2,
3,
1,
1,
0,
1,
2,
1,
1,
-1,
1,
2,
2,
0,
0,
0,
2,
3,
1,
0,
1,
1,
2,
1,
-1,
1,
1,
2,
2,
0,
0,
0,
2,
3,
1,
1,
1,
0,
2,
1,
1,
1,
-1,
2,
0,
2,
0,
0,
2,
3,
1,
1,
0,
1,
2,
1,
1,
-1,
1,
2,
0,
2,
0,
0,
2,
3,
0,
1,
1,
1,
2,
-1,
1,
1,
1,
2,
0,
2,
0,
0,
2,
3,
1,
1,
1,
0,
2,
1,
1,
1,
-1,
2,
0,
0,
2,
0,
2,
3,
1,
0,
1,
1,
2,
1,
-1,
1,
1,
2,
0,
0,
2,
0,
2,
3,
0,
1,
1,
1,
2,
-1,
1,
1,
1,
2,
0,
0,
2,
0,
2,
3,
1,
1,
0,
1,
2,
1,
1,
-1,
1,
2,
0,
0,
0,
2,
2,
3,
1,
0,
1,
1,
2,
1,
-1,
1,
1,
2,
0,
0,
0,
2,
2,
3,
0,
1,
1,
1,
2,
-1,
1,
1,
1,
2,
0,
0,
0,
2,
2,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
0,
0,
0,
0,
0,
2,
1,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
0,
0,
0,
0,
0,
2,
1,
-1,
1,
1,
0,
1,
0,
1,
1,
-1,
0,
0,
0,
0,
0,
2,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
0,
0,
0,
0,
0,
2,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
1,
0,
0,
0,
0,
0,
2,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
1,
0,
0,
0,
0,
0,
2,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
2,
2,
0,
0,
0,
2,
1,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
2,
2,
0,
0,
0,
2,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
2,
2,
0,
0,
0,
2,
1,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
2,
0,
2,
0,
0,
2,
1,
-1,
1,
1,
0,
1,
0,
1,
1,
-1,
2,
0,
2,
0,
0,
2,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
1,
2,
0,
2,
0,
0,
2,
1,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
2,
0,
0,
2,
0,
2,
1,
-1,
1,
1,
0,
1,
0,
1,
1,
-1,
2,
0,
0,
2,
0,
2,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
1,
2,
0,
0,
2,
0,
2,
1,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
2,
0,
0,
0,
2,
2,
1,
-1,
1,
0,
1,
1,
0,
1,
-1,
1,
2,
0,
0,
0,
2,
2,
1,
-1,
0,
1,
1,
1,
0,
-1,
1,
1,
2,
0,
0,
0,
2,
3,
1,
1,
0,
0,
0,
2,
2,
0,
0,
0,
2,
1,
1,
1,
-1,
3,
1,
0,
1,
0,
0,
2,
0,
2,
0,
0,
2,
1,
1,
1,
-1,
3,
1,
0,
0,
1,
0,
2,
0,
0,
2,
0,
2,
1,
1,
1,
-1,
3,
1,
1,
0,
0,
0,
2,
2,
0,
0,
0,
2,
1,
1,
-1,
1,
3,
1,
0,
1,
0,
0,
2,
0,
2,
0,
0,
2,
1,
1,
-1,
1,
3,
1,
0,
0,
0,
1,
2,
0,
0,
0,
2,
2,
1,
1,
-1,
1,
3,
1,
1,
0,
0,
0,
2,
2,
0,
0,
0,
2,
1,
-1,
1,
1,
3,
1,
0,
0,
1,
0,
2,
0,
0,
2,
0,
2,
1,
-1,
1,
1,
3,
1,
0,
0,
0,
1,
2,
0,
0,
0,
2,
2,
1,
-1,
1,
1,
3,
1,
0,
1,
0,
0,
2,
0,
2,
0,
0,
2,
-1,
1,
1,
1,
3,
1,
0,
0,
1,
0,
2,
0,
0,
2,
0,
2,
-1,
1,
1,
1,
3,
1,
0,
0,
0,
1,
2,
0,
0,
0,
2,
2,
-1,
1,
1,
1,
3,
3,
2,
1,
0,
0,
3,
1,
2,
0,
0,
4,
1,
1,
1,
1,
3,
3,
2,
0,
1,
0,
3,
1,
0,
2,
0,
4,
1,
1,
1,
1,
3,
3,
0,
2,
1,
0,
3,
0,
1,
2,
0,
4,
1,
1,
1,
1,
3,
3,
2,
0,
0,
1,
3,
1,
0,
0,
2,
4,
1,
1,
1,
1,
3,
3,
0,
2,
0,
1,
3,
0,
1,
0,
2,
4,
1,
1,
1,
1,
3,
3,
0,
0,
2,
1,
3,
0,
0,
1,
2,
4,
1,
1,
1,
1,
3,
3,
2,
1,
0,
0,
3,
1,
2,
0,
0,
2,
1,
1,
1,
-1,
3,
3,
2,
0,
1,
0,
3,
1,
0,
2,
0,
2,
1,
1,
1,
-1,
3,
3,
0,
2,
1,
0,
3,
0,
1,
2,
0,
2,
1,
1,
1,
-1,
3,
3,
2,
1,
0,
0,
3,
1,
2,
0,
0,
2,
1,
1,
-1,
1,
3,
3,
2,
0,
0,
1,
3,
1,
0,
0,
2,
2,
1,
1,
-1,
1,
3,
3,
0,
2,
0,
1,
3,
0,
1,
0,
2,
2,
1,
1,
-1,
1,
3,
3,
2,
0,
1,
0,
3,
1,
0,
2,
0,
2,
1,
-1,
1,
1,
3,
3,
2,
0,
0,
1,
3,
1,
0,
0,
2,
2,
1,
-1,
1,
1,
3,
3,
0,
0,
2,
1,
3,
0,
0,
1,
2,
2,
1,
-1,
1,
1,
3,
3,
0,
2,
1,
0,
3,
0,
1,
2,
0,
2,
-1,
1,
1,
1,
3,
3,
0,
2,
0,
1,
3,
0,
1,
0,
2,
2,
-1,
1,
1,
1,
3,
3,
0,
0,
2,
1,
3,
0,
0,
1,
2,
2,
-1,
1,
1,
1,
];
}
noise(x, y, z, w) {
const perm = this.#perm, perm4D = this.#perm4D, lookup = this.#lookup, STRETCH_4D = this.#STRETCH_4D, SQUISH_4D = this.#SQUISH_4D, gradients4D = this.#gradients4D, NORM_4D = this.#NORM_4D, stretchOffset = (x + y + z + w) * STRETCH_4D, xs = x + stretchOffset, ys = y + stretchOffset, zs = z + stretchOffset, ws = w + stretchOffset, xsb = Math.floor(xs), ysb = Math.floor(ys), zsb = Math.floor(zs), wsb = Math.floor(ws), squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_4D, dx0 = x - (xsb + squishOffset), dy0 = y - (ysb + squishOffset), dz0 = z - (zsb + squishOffset), dw0 = w - (wsb + squishOffset), xins = xs - xsb, yins = ys - ysb, zins = zs - zsb, wins = ws - wsb, inSum = xins + yins + zins + wins, hash = (zins - wins + 1) |
((yins - zins + 1) << 1) |
((yins - wins + 1) << 2) |
((xins - yins + 1) << 3) |
((xins - zins + 1) << 4) |
((xins - wins + 1) << 5) |
(inSum << 6) |
((inSum + wins) << 8) |
((inSum + zins) << 11) |
((inSum + yins) << 14) |
((inSum + xins) << 17);
let value = 0;
for (let c = lookup[hash]; c !== undefined; c = c.next) {
const dx = dx0 + c.dx, dy = dy0 + c.dy, dz = dz0 + c.dz, dw = dw0 + c.dw, attn = 2 - dx * dx - dy * dy - dz * dz - dw * dw;
if (attn > 0) {
const px = xsb + c.xsb, py = ysb + c.ysb, pz = zsb + c.zsb, pw = wsb + c.wsb, indexPartA = perm[px & 0xff], indexPartB = perm[(indexPartA + py) & 0xff], indexPartC = perm[(indexPartB + pz) & 0xff], index = perm4D[(indexPartC + pw) & 0xff], valuePart = gradients4D[index] * dx +
gradients4D[index + 1] * dy +
gradients4D[index + 2] * dz +
gradients4D[index + 3] * dw;
value += attn * attn * attn * attn * valuePart;
}
}
return value * NORM_4D;
}
seed(clientSeed) {
const p4D = this.#p4D, base4D = this.#base4D, lookupPairs4D = this.#lookupPairs4D, contributions = [];
for (let i = 0; i < p4D.length; i += 16) {
const baseSet = base4D[p4D[i]];
let previous = null, current = null;
for (let k = 0; k < baseSet.length; k += 5) {
current = this.#contribution4D(baseSet[k], baseSet[k + 1], baseSet[k + 2], baseSet[k + 3], baseSet[k + 4]);
if (previous === null) {
contributions[i / 16] = current;
}
else {
previous.next = current;
}
previous = current;
}
if (current) {
current.next = this.#contribution4D(p4D[i + 1], p4D[i + 2], p4D[i + 3], p4D[i + 4], p4D[i + 5]);
current.next.next = this.#contribution4D(p4D[i + 6], p4D[i + 7], p4D[i + 8], p4D[i + 9], p4D[i + 10]);
current.next.next.next = this.#contribution4D(p4D[i + 11], p4D[i + 12], p4D[i + 13], p4D[i + 14], p4D[i + 15]);
}
}
this.#lookup = [];
for (let i = 0; i < lookupPairs4D.length; i += 2) {
this.#lookup[lookupPairs4D[i]] = contributions[lookupPairs4D[i + 1]];
}
this.#perm = new Uint8Array(256);
this.#perm4D = new Uint8Array(256);
const source = new Uint8Array(256);
for (let i = 0; i < 256; i++) {
source[i] = i;
}
let seed = new Uint32Array(1);
seed[0] = clientSeed;
seed = shuffleSeed(shuffleSeed(shuffleSeed(seed)));
for (let i = 255; i >= 0; i--) {
seed = shuffleSeed(seed);
const r = new Uint32Array(1);
r[0] = (seed[0] + 31) % (i + 1);
if (r[0] < 0) {
r[0] += i + 1;
}
this.#perm[i] = source[r[0]];
this.#perm4D[i] = this.#perm[i] & 0xfc;
source[r[0]] = source[i];
}
}
#contribution4D(multiplier, xsb, ysb, zsb, wsb) {
const SQUISH_4D = this.#SQUISH_4D;
return {
dx: -xsb - multiplier * SQUISH_4D,
dy: -ysb - multiplier * SQUISH_4D,
dz: -zsb - multiplier * SQUISH_4D,
dw: -wsb - multiplier * SQUISH_4D,
xsb,
ysb,
zsb,
wsb,
};
}
}
class SimplexNoise {
noise2d;
noise3d;
noise4d;
constructor() {
this.noise2d = new SimplexNoise2D();
this.noise3d = new SimplexNoise3D();
this.noise4d = new SimplexNoise4D();
}
}
const defaultOptions$5 = {
speed: 0.2,
step: 250,
};
class CurlNoiseGenerator {
options;
#container;
#res;
#simplex;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
const simplex = new SimplexNoise();
this.#simplex = simplex.noise2d;
this.options = deepExtend({}, defaultOptions$5);
}
generate(particle) {
const pos = particle.getPosition(), { speed, step } = this.options, x = pos.x / step, y = pos.y / step, eps = 0.001, n1a = this.#simplex.noise(x, y + eps), n2a = this.#simplex.noise(x, y - eps), a = (n1a - n2a) / (double * eps), n1b = this.#simplex.noise(x + eps, y), n2b = this.#simplex.noise(x - eps, y), b = (n1b - n2b) / (double * eps);
particle.velocity.x = 0;
particle.velocity.y = 0;
this.#res.x = speed * a;
this.#res.y = speed * -b;
return this.#res;
}
init() {
const container = this.#container, sourceOptions = container.actualOptions.particles.move.path.options;
this.options.seed = sourceOptions["seed"];
this.options.speed =
(sourceOptions["speed"] ?? defaultOptions$5.speed) * container.retina.pixelRatio;
this.options.step = sourceOptions["step"] ?? defaultOptions$5.step;
this.#simplex.seed(this.options.seed ?? getRandom());
}
reset() {
}
update() {
}
}
const curlNoisePathName = "curlNoise";
async function loadCurlNoisePath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(curlNoisePathName, container => {
return Promise.resolve(new CurlNoiseGenerator(container));
});
});
}
function CurvesPathGen(rndFunc, period, nbHarmonics, attenHarmonics, lowValue = 0, highValue = 1) {
const arP0 = [], arP1 = [], amplitudes = [], increments = [], phases = [], randomFunc = rndFunc ?? getRandom;
let globAmplitude = 0;
if (nbHarmonics < 1)
nbHarmonics = 1;
for (let kh = 1; kh <= nbHarmonics; ++kh) {
arP0[kh] = randomFunc();
arP1[kh] = randomFunc();
amplitudes[kh] = kh === 1 ? 1 : amplitudes[kh - 1] * attenHarmonics;
globAmplitude += amplitudes[kh];
increments[kh] = kh / period;
phases[kh] = randomFunc();
}
amplitudes.forEach((value, kh) => (amplitudes[kh] = (value / globAmplitude) * (highValue - lowValue)));
return () => {
let pf, pfl, signal = 0;
for (let kh = nbHarmonics; kh >= 1; --kh) {
pf = phases[kh] += increments[kh];
if (phases[kh] >= 1) {
pf = phases[kh] -= 1;
arP0[kh] = arP1[kh];
arP1[kh] = randomFunc();
}
pfl = pf ** 2 * (3 - 2 * pf);
signal += (arP0[kh] * (1 - pfl) + arP1[kh] * pfl) * amplitudes[kh];
}
return signal + lowValue;
};
}
const defaultOptions$4 = {
rndFunc: null,
period: 100,
nbHarmonics: 2,
attenHarmonics: 0.8,
lowValue: -0.03,
highValue: 0.03,
};
function randomVelocity() {
const offset = 0.8, factor = 0.6;
return getRandom() * factor + offset;
}
class CurvesPathGenerator {
options;
#container;
constructor(container) {
this.#container = container;
this.options = deepExtend({}, defaultOptions$4);
}
generate(particle) {
if (!particle.pathGen) {
const options = this.options;
particle.pathGen = CurvesPathGen(options.rndFunc, options.period, options.nbHarmonics, options.attenHarmonics, options.lowValue, options.highValue);
}
if (particle.curveVelocity) {
particle.curveVelocity.length += 0.01;
particle.curveVelocity.angle = (particle.curveVelocity.angle + particle.pathGen()) % doublePI;
}
else {
particle.curveVelocity = Vector.origin;
particle.curveVelocity.length = randomVelocity();
particle.curveVelocity.angle = getRandom() * doublePI;
}
particle.velocity.x = 0;
particle.velocity.y = 0;
return particle.curveVelocity;
}
init() {
const sourceOptions = this.#container.actualOptions.particles.move.path.options;
if (isFunction(sourceOptions["rndFunc"])) {
this.options.rndFunc = sourceOptions["rndFunc"];
}
else if (isString(sourceOptions["rndFunc"])) {
this.options.rndFunc =
globalThis[sourceOptions["rndFunc"]] ??
this.options.rndFunc;
}
this.options.period = sourceOptions["period"] ?? this.options.period;
this.options.nbHarmonics = sourceOptions["nbHarmonics"] ?? this.options.nbHarmonics;
this.options.attenHarmonics =
sourceOptions["attenHarmonics"] ?? this.options.attenHarmonics;
this.options.lowValue = sourceOptions["lowValue"] ?? this.options.lowValue;
this.options.highValue = sourceOptions["highValue"] ?? this.options.highValue;
}
reset(particle) {
delete particle.pathGen;
delete particle.curveVelocity;
}
update() {
}
}
const curvesPathName = "curvesPathGenerator";
async function loadCurvesPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(curvesPathName, container => {
return Promise.resolve(new CurvesPathGenerator(container));
});
});
}
const easingsFunctions$d = new Map();
easingsFunctions$d.set("ease-in-back", value => {
const c1 = 1.70158, c3 = c1 + 1;
return c3 * value ** 3 - c1 * value ** 2;
});
easingsFunctions$d.set("ease-out-back", value => {
const c1 = 1.70158, c3 = c1 + 1;
return 1 + c3 * Math.pow(value - 1, 3) + c1 * Math.pow(value - 1, 2);
});
easingsFunctions$d.set("ease-in-out-back", value => {
const c1 = 1.70158, c2 = c1 * 1.525;
return value < 0.5
? ((2 * value) ** 2 * ((c2 + 1) * 2 * value - c2)) / 2
: ((2 * value - 2) ** 2 * ((c2 + 1) * (value * 2 - 2) + c2) + 2) / 2;
});
async function loadEasingBackPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$d) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$c = new Map(), bounceOut = (t) => {
const n1 = 7.5625, d1 = 2.75;
if (t < 1 / d1) {
return n1 * t * t;
}
else if (t < 2 / d1) {
t -= 1.5 / d1;
return n1 * t * t + 0.75;
}
else if (t < 2.5 / d1) {
t -= 2.25 / d1;
return n1 * t * t + 0.9375;
}
else {
t -= 2.625 / d1;
return n1 * t * t + 0.984375;
}
};
easingsFunctions$c.set("ease-out-bounce", value => bounceOut(value));
easingsFunctions$c.set("ease-in-bounce", value => 1 - bounceOut(1 - value));
easingsFunctions$c.set("ease-in-out-bounce", value => value < 0.5 ? (1 - bounceOut(1 - value * 2)) / 2 : (1 + bounceOut(value * 2 - 1)) / 2);
async function loadEasingBouncePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$c) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$b = new Map();
easingsFunctions$b.set("ease-in-circ", value => 1 - Math.sqrt(1 - value ** 2));
easingsFunctions$b.set("ease-out-circ", value => Math.sqrt(1 - (value - 1) ** 2));
easingsFunctions$b.set("ease-in-out-circ", value => value < 0.5 ? (1 - Math.sqrt(1 - (2 * value) ** 2)) / 2 : (Math.sqrt(1 - (-2 * value + 2) ** 2) + 1) / 2);
async function loadEasingCircPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$b) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$a = new Map();
easingsFunctions$a.set("ease-in-cubic", value => value ** 3);
easingsFunctions$a.set("ease-out-cubic", value => 1 - (1 - value) ** 3);
easingsFunctions$a.set("ease-in-out-cubic", value => (value < 0.5 ? 4 * value ** 3 : 1 - (-2 * value + 2) ** 3 / 2));
async function loadEasingCubicPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$a) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$9 = new Map();
easingsFunctions$9.set("ease-in-elastic", value => {
if (value === 0 || value === 1) {
return value;
}
return -Math.pow(2, 10 * (value - 1)) * Math.sin(((value - 1.075) * (2 * Math.PI)) / 0.3);
});
easingsFunctions$9.set("ease-out-elastic", value => {
if (value === 0 || value === 1) {
return value;
}
return Math.pow(2, -10 * value) * Math.sin(((value - 0.075) * (2 * Math.PI)) / 0.3) + 1;
});
easingsFunctions$9.set("ease-in-out-elastic", value => {
if (value === 0 || value === 1) {
return value;
}
value *= 2;
if (value < 1) {
return -0.5 * Math.pow(2, 10 * (value - 1)) * Math.sin(((value - 1.1125) * (2 * Math.PI)) / 0.45);
}
return Math.pow(2, -10 * (value - 1)) * Math.sin(((value - 1.1125) * (2 * Math.PI)) / 0.45) * 0.5 + 1;
});
async function loadEasingElasticPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$9) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$8 = new Map();
easingsFunctions$8.set("ease-in-expo", value => (value ? 2 ** (10 * value - 10) : 0));
easingsFunctions$8.set("ease-out-expo", value => (value === 1 ? 1 : 1 - Math.pow(2, -10 * value)));
easingsFunctions$8.set("ease-in-out-expo", value => {
if (value === 1) {
return 1;
}
else if (value) {
return value < 0.5 ? 2 ** (20 * value - 10) / 2 : (2 - 2 ** (-20 * value + 10)) / 2;
}
else {
return 0;
}
});
async function loadEasingExpoPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$8) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$7 = new Map(), gaussian = (t) => Math.exp(-4 * (1 - t) * (1 - t));
easingsFunctions$7.set("ease-in-gaussian", value => gaussian(value));
easingsFunctions$7.set("ease-out-gaussian", value => gaussian(value));
easingsFunctions$7.set("ease-in-out-gaussian", value => gaussian(value));
async function loadEasingGaussianPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$7) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$6 = new Map();
easingsFunctions$6.set("ease-in-linear", value => value);
easingsFunctions$6.set("ease-out-linear", value => value);
easingsFunctions$6.set("ease-in-out-linear", value => value);
async function loadEasingLinearPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$6) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$5 = new Map();
easingsFunctions$5.set("ease-in-quart", value => value ** 4);
easingsFunctions$5.set("ease-out-quart", value => 1 - (1 - value) ** 4);
easingsFunctions$5.set("ease-in-out-quart", value => (value < 0.5 ? 8 * value ** 4 : 1 - (-2 * value + 2) ** 4 / 2));
async function loadEasingQuartPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$5) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$4 = new Map();
easingsFunctions$4.set("ease-in-quint", value => value ** 5);
easingsFunctions$4.set("ease-out-quint", value => 1 - (1 - value) ** 5);
easingsFunctions$4.set("ease-in-out-quint", value => (value < 0.5 ? 16 * value ** 5 : 1 - (-2 * value + 2) ** 5 / 2));
async function loadEasingQuintPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$4) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$3 = new Map(), sigmoid = (t) => 1 / (1 + Math.exp(-10 * (t - 0.5)));
easingsFunctions$3.set("ease-in-sigmoid", value => sigmoid(value));
easingsFunctions$3.set("ease-out-sigmoid", value => sigmoid(value));
easingsFunctions$3.set("ease-in-out-sigmoid", value => sigmoid(value));
async function loadEasingSigmoidPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$3) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$2 = new Map();
easingsFunctions$2.set("ease-in-sine", value => 1 - Math.cos((value * Math.PI) / 2));
easingsFunctions$2.set("ease-out-sine", value => Math.sin((value * Math.PI) / 2));
easingsFunctions$2.set("ease-in-out-sine", value => -(Math.cos(Math.PI * value) - 1) / 2);
async function loadEasingSinePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$2) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const easingsFunctions$1 = new Map(), smoothstep$1 = (t) => t * t * (3 - 2 * t);
easingsFunctions$1.set("ease-in-smoothstep", value => smoothstep$1(value));
easingsFunctions$1.set("ease-out-smoothstep", value => smoothstep$1(value));
easingsFunctions$1.set("ease-in-out-smoothstep", value => smoothstep$1(value));
async function loadEasingSmoothstepPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions$1) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
class ClickEvent {
enable = false;
mode = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "mode", data.mode);
}
}
var DivType;
(function (DivType) {
DivType["circle"] = "circle";
DivType["rectangle"] = "rectangle";
})(DivType || (DivType = {}));
class DivEvent {
enable = false;
mode = [];
selectors = [];
type = DivType.circle;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "selectors", data.selectors);
loadProperty(this, "enable", data.enable);
loadProperty(this, "mode", data.mode);
loadProperty(this, "type", data.type);
}
}
class HoverEvent {
enable = false;
mode = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "mode", data.mode);
}
}
class Events {
onClick = new ClickEvent();
onDiv = new DivEvent();
onHover = new HoverEvent();
load(data) {
if (isNull(data)) {
return;
}
this.onClick.load(data.onClick);
const onDiv = data.onDiv;
if (onDiv !== undefined) {
this.onDiv = executeOnSingleOrMultiple(onDiv, t => {
const tmp = new DivEvent();
tmp.load(t);
return tmp;
});
}
this.onHover.load(data.onHover);
}
}
var InteractivityDetect;
(function (InteractivityDetect) {
InteractivityDetect["canvas"] = "canvas";
InteractivityDetect["parent"] = "parent";
InteractivityDetect["window"] = "window";
})(InteractivityDetect || (InteractivityDetect = {}));
class Modes {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
load(data) {
if (isNull(data)) {
return;
}
if (!this.#container) {
return;
}
const interactors = this.#pluginManager.interactors?.get(this.#container);
if (!interactors) {
return;
}
for (const interactor of interactors) {
if (!interactor.loadModeOptions) {
continue;
}
interactor.loadModeOptions(this, data);
}
}
}
class Interactivity {
detectsOn = InteractivityDetect.window;
events = new Events();
modes;
constructor(pluginManager, container) {
this.modes = new Modes(pluginManager, container);
}
load(data) {
if (isNull(data)) {
return;
}
const detectsOn = data.detectsOn;
if (detectsOn !== undefined) {
this.detectsOn = detectsOn;
}
this.events.load(data.events);
this.modes.load(data.modes);
}
}
class InteractivityPlugin {
id = "interactivity";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { InteractivityPluginInstance } = await Promise.resolve().then(function () { return InteractivityPluginInstance$1; });
return new InteractivityPluginInstance(this.#pluginManager, container);
}
loadOptions(container, options, source) {
if (!this.needsPlugin()) {
return;
}
let interactivityOptions = options.interactivity;
if (!interactivityOptions?.load) {
options.interactivity = interactivityOptions = new Interactivity(this.#pluginManager, container);
}
interactivityOptions.load(source?.interactivity);
const interactors = this.#pluginManager.interactors?.get(container);
if (!interactors) {
return;
}
for (const interactor of interactors) {
if (interactor.loadOptions) {
interactor.loadOptions(options, source);
}
}
}
loadParticlesOptions(container, options, source) {
if (source?.interactivity) {
options.interactivity = deepExtend({}, source.interactivity);
}
const interactors = this.#pluginManager.interactors?.get(container);
if (!interactors) {
return;
}
for (const interactor of interactors) {
interactor.loadParticlesOptions?.(options, source);
}
}
needsPlugin() {
return true;
}
}
var InteractorType;
(function (InteractorType) {
InteractorType["external"] = "external";
InteractorType["particles"] = "particles";
})(InteractorType || (InteractorType = {}));
class ExternalInteractorBase {
type = InteractorType.external;
container;
constructor(container) {
this.container = container;
}
}
class ParticlesInteractorBase {
type = InteractorType.particles;
container;
constructor(container) {
this.container = container;
}
}
const clickEvent = "click", mouseDownEvent$1 = "pointerdown", mouseUpEvent = "pointerup", mouseLeaveEvent = "pointerleave", mouseMoveEvent = "pointermove", touchStartEvent$1 = "touchstart", touchEndEvent = "touchend", touchMoveEvent = "touchmove", touchCancelEvent = "touchcancel";
function findItemFromSingleOrMultiple(obj, callback) {
if (isArray(obj)) {
return obj.find((t, index) => callback(t, index));
}
const defaultIndex = 0;
return callback(obj, defaultIndex) ? obj : undefined;
}
function checkSelector(element, selectors) {
const res = executeOnSingleOrMultiple(selectors, selector => {
return element.matches(selector);
});
return isArray(res) ? res.some(t => t) : res;
}
function isDivModeEnabled(mode, divs) {
return !!findItemFromSingleOrMultiple(divs, t => t.enable && isInArray(mode, t.mode));
}
function divModeExecute(mode, divs, callback) {
executeOnSingleOrMultiple(divs, div => {
const divMode = div.mode, divEnabled = div.enable;
if (divEnabled && isInArray(mode, divMode)) {
singleDivModeExecute(div, callback);
}
});
}
function singleDivModeExecute(div, callback) {
const selectors = div.selectors;
executeOnSingleOrMultiple(selectors, selector => {
callback(selector, div);
});
}
function divMode(divs, element) {
if (!element || !divs) {
return;
}
return findItemFromSingleOrMultiple(divs, div => {
return checkSelector(element, div.selectors);
});
}
async function loadInteractivityPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
const interactivityEngine = e, interactivityPluginManager = interactivityEngine.pluginManager;
interactivityPluginManager.addPlugin(new InteractivityPlugin(interactivityPluginManager));
interactivityPluginManager.initializers.interactors ??= new Map();
interactivityPluginManager.interactors ??= new Map();
interactivityPluginManager.addInteractor = (name, interactorInitializer) => {
interactivityPluginManager.initializers.interactors ??= new Map();
interactivityPluginManager.initializers.interactors.set(name, interactorInitializer);
};
interactivityPluginManager.getInteractors = async (container, force = false) => {
interactivityPluginManager.interactors ??= new Map();
interactivityPluginManager.initializers.interactors ??= new Map();
return getItemsFromInitializer(container, interactivityPluginManager.interactors, interactivityPluginManager.initializers.interactors, force);
};
interactivityPluginManager.setOnClickHandler = (callback) => {
const { items } = interactivityEngine;
if (!items.length) {
throw new Error("Click handlers can only be set after calling tsParticles.load()");
}
items.forEach(item => {
const interactivityContainer = item;
interactivityContainer.addClickHandler?.(callback);
});
};
});
}
function ensureInteractivityPluginLoaded(e) {
if (!e.pluginManager.addInteractor) {
throw new Error("tsParticles Interactivity Plugin is not loaded");
}
}
class EmitterLife {
count;
delay;
duration;
wait = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "count", data.count);
loadRangeProperty(this, "delay", data.delay);
loadRangeProperty(this, "duration", data.duration);
loadProperty(this, "wait", data.wait);
}
}
class EmitterRate {
delay = 0.1;
quantity = 1;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "quantity", data.quantity);
loadRangeProperty(this, "delay", data.delay);
}
}
class EmitterShapeReplace {
color = false;
opacity = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "color", data.color);
loadProperty(this, "opacity", data.opacity);
}
}
class EmitterShape {
options = {};
replace = new EmitterShapeReplace();
type = "square";
load(data) {
if (isNull(data)) {
return;
}
if (data.options !== undefined) {
this.options = deepExtend({}, data.options ?? {});
}
this.replace.load(data.replace);
loadProperty(this, "type", data.type);
}
}
class EmitterSize {
height = 0;
mode = exports.PixelMode.percent;
width = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "mode", data.mode);
loadProperty(this, "height", data.height);
loadProperty(this, "width", data.width);
}
}
class EmitterSpawn {
fill;
stroke;
load(data) {
if (isNull(data)) {
return;
}
if (data.fill) {
this.fill ??= new Fill();
this.fill.load(data.fill);
}
if (data.stroke) {
this.stroke ??= new Stroke();
this.stroke.load(data.stroke);
}
}
}
class Emitter {
autoPlay = true;
direction;
domId;
fill = true;
life = new EmitterLife();
name;
particles;
position;
rate = new EmitterRate();
shape = new EmitterShape();
size;
spawn = new EmitterSpawn();
spawnFillColor;
spawnStrokeColor;
startCount = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "autoPlay", data.autoPlay);
if (data.size !== undefined) {
this.size ??= new EmitterSize();
this.size.load(data.size);
}
loadProperty(this, "direction", data.direction);
loadProperty(this, "domId", data.domId);
loadProperty(this, "fill", data.fill);
this.life.load(data.life);
loadProperty(this, "name", data.name);
this.particles = executeOnSingleOrMultiple(data.particles, particles => {
return deepExtend({}, particles);
});
this.rate.load(data.rate);
this.shape.load(data.shape);
this.spawn.load(data.spawn);
if (data.position !== undefined) {
this.position = {};
if (data.position.x !== undefined) {
this.position.x = setRangeValue(data.position.x);
}
if (data.position.y !== undefined) {
this.position.y = setRangeValue(data.position.y);
}
}
if (data.spawnFillColor !== undefined) {
this.spawnFillColor ??= new AnimatableColor();
this.spawnFillColor.load(data.spawnFillColor);
}
if (data.spawnStrokeColor !== undefined) {
this.spawnStrokeColor ??= new AnimatableColor();
this.spawnStrokeColor.load(data.spawnStrokeColor);
}
loadProperty(this, "startCount", data.startCount);
}
}
const defaultRandomOptions = {
count: 1,
enable: true,
};
const emittersMode = "emitters";
class EmittersInteractor extends ExternalInteractorBase {
handleClickMode;
maxDistance;
#instancesManager;
constructor(instancesManager, container) {
super(container);
this.#instancesManager = instancesManager;
this.maxDistance = 0;
this.handleClickMode = (mode, interactivityData) => {
const container = this.container, options = container.actualOptions, modeEmitters = options.interactivity.modes.emitters;
if (!modeEmitters || mode !== emittersMode) {
return;
}
let emittersModeOptions;
if (isArray(modeEmitters.value)) {
const minLength = 0, modeEmittersCount = modeEmitters.value.length;
if (modeEmittersCount > minLength && modeEmitters.random.enable) {
emittersModeOptions = [];
const usedIndexes = new Set();
for (let i = 0; i < modeEmitters.random.count; i++) {
const idx = Math.floor(getRandom() * modeEmitters.value.length);
if (usedIndexes.has(idx) && usedIndexes.size < modeEmittersCount) {
i--;
continue;
}
usedIndexes.add(idx);
const selectedOptions = itemFromArray(modeEmitters.value, idx);
if (!selectedOptions) {
continue;
}
emittersModeOptions.push(selectedOptions);
}
}
else {
emittersModeOptions = modeEmitters.value;
}
}
else {
emittersModeOptions = modeEmitters.value;
}
const emittersOptions = emittersModeOptions, ePosition = interactivityData.mouse.clickPosition;
void executeOnSingleOrMultiple(emittersOptions, async (emitter) => {
await this.#instancesManager.addEmitter(this.container, emitter, ePosition);
});
};
}
clear() {
}
init() {
}
interact(_interactivityData, delta) {
for (const emitter of this.#instancesManager.getArray(this.container)) {
emitter.update(delta);
}
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity).events;
if (!mouse.clickPosition || !events.onClick.enable) {
return false;
}
return isInArray(emittersMode, events.onClick.mode);
}
loadModeOptions(options, ...sources) {
options.emitters = {
random: defaultRandomOptions,
value: [],
};
for (const source of sources) {
if (!source?.emitters) {
continue;
}
if (isArray(source.emitters)) {
for (const emitter of source.emitters) {
const tmp = new Emitter();
tmp.load(emitter);
options.emitters.value.push(tmp);
}
}
else if ("value" in source.emitters) {
const emitterModeOptions = source.emitters;
options.emitters.random.enable = emitterModeOptions.random?.enable ?? options.emitters.random.enable;
options.emitters.random.count = emitterModeOptions.random?.count ?? options.emitters.random.count;
if (isArray(emitterModeOptions.value)) {
for (const emitter of emitterModeOptions.value) {
const tmp = new Emitter();
tmp.load(emitter);
options.emitters.value.push(tmp);
}
}
else {
const tmp = new Emitter();
tmp.load(emitterModeOptions.value);
options.emitters.value.push(tmp);
}
}
else {
const tmp = new Emitter();
tmp.load(source.emitters);
options.emitters.value.push(tmp);
}
}
}
removeEmitter(emitter) {
const index = this.#instancesManager.getArray(this.container).indexOf(emitter), minIndex = 0, deleteCount = 1;
if (index >= minIndex) {
this.#instancesManager.getArray(this.container).splice(index, deleteCount);
}
}
reset() {
}
}
async function addEmittersShapesManager(e) {
const { ShapeManager } = await Promise.resolve().then(function () { return ShapeManager$1; }), pluginManager = e.pluginManager;
pluginManager.emitterShapeManager ??= new ShapeManager();
pluginManager.addEmitterShapeGenerator ??= (name, generator) => {
pluginManager.emitterShapeManager?.addShapeGenerator(name, generator);
};
}
const instancesManagers$1 = new WeakMap();
function getEmittersInstancesManager(e) {
const pluginManager = e.pluginManager;
let manager = instancesManagers$1.get(pluginManager);
if (!manager) {
manager = Promise.resolve().then(function () { return EmittersInstancesManager$1; })
.then(({ EmittersInstancesManager }) => new EmittersInstancesManager(pluginManager))
.catch((error) => {
instancesManagers$1.delete(pluginManager);
throw error;
});
instancesManagers$1.set(pluginManager, manager);
}
return manager;
}
class EmitterShapeBase {
fill;
options;
position;
size;
constructor(position, size, fill, options) {
this.position = position;
this.size = size;
this.fill = fill;
this.options = options;
}
resize(position, size) {
this.position = position;
this.size = size;
}
}
var EmitterClickMode;
(function (EmitterClickMode) {
EmitterClickMode["emitter"] = "emitter";
})(EmitterClickMode || (EmitterClickMode = {}));
async function loadEmittersInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const instancesManager = await getEmittersInstancesManager(e);
ensureInteractivityPluginLoaded(e);
await addEmittersShapesManager(e);
e.pluginManager.addInteractor?.("externalEmitters", container => {
return Promise.resolve(new EmittersInteractor(instancesManager, container));
});
});
}
class EmittersPlugin {
id = "emitters";
#instancesManager;
constructor(instancesManager) {
this.#instancesManager = instancesManager;
}
async getPlugin(container) {
const { EmittersPluginInstance } = await Promise.resolve().then(function () { return EmittersPluginInstance$1; });
return new EmittersPluginInstance(this.#instancesManager, container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
if (source?.emitters) {
options.emitters = executeOnSingleOrMultiple(source.emitters, emitter => {
const tmp = new Emitter();
tmp.load(emitter);
return tmp;
});
}
}
needsPlugin(options) {
if (!options) {
return false;
}
const emitters = options.emitters;
return (isArray(emitters) && !!emitters.length) || emitters !== undefined;
}
}
async function loadEmittersPluginSimple(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const instancesManager = await getEmittersInstancesManager(e);
await addEmittersShapesManager(e);
e.pluginManager.addPlugin(new EmittersPlugin(instancesManager));
});
}
function ensureEmittersPluginLoaded(e) {
if (!e.pluginManager.addEmitterShapeGenerator) {
throw new Error("tsParticles Emitters Plugin is not loaded");
}
}
async function loadEmittersPlugin(engine) {
await loadEmittersPluginSimple(engine);
await loadEmittersInteraction(engine);
}
const defaultWidth$3 = 0, indexesOffset = {
r: 0,
g: 1,
b: 2,
a: 3,
}, alphaMax = 255, alphaFactor$1 = identity$3 / alphaMax;
function drawLine$1(context, begin, end) {
context.beginPath();
context.moveTo(begin.x, begin.y);
context.lineTo(end.x, end.y);
context.closePath();
}
function getCanvasImageData(ctx, size, offset, clear = true) {
const imageData = ctx.getImageData(originPoint.x, originPoint.y, size.width, size.height).data;
if (clear) {
ctx.clearRect(originPoint.x, originPoint.y, size.width, size.height);
}
const pixels = [];
for (let i = 0; i < imageData.length; i += offset) {
const idx = i / offset, pos = {
x: idx % size.width,
y: Math.floor(idx / size.width),
};
pixels[pos.y] ??= [];
const row = pixels[pos.y];
if (!row) {
continue;
}
row[pos.x] = {
r: imageData[i + indexesOffset.r] ?? defaultRgbMin,
g: imageData[i + indexesOffset.g] ?? defaultRgbMin,
b: imageData[i + indexesOffset.b] ?? defaultRgbMin,
a: (imageData[i + indexesOffset.a] ?? defaultAlpha$1) * alphaFactor$1,
};
}
return {
pixels,
width: Math.min(...pixels.map(row => row.length)),
height: pixels.length,
};
}
function getImageData(src, offset, canvasSettings) {
const image = new Image();
image.crossOrigin = "Anonymous";
const p = new Promise((resolve, reject) => {
image.onerror = reject;
image.onload = () => {
const canvas = safeDocument().createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext("2d", canvasSettings);
if (!context) {
reject(new Error("Could not get canvas context"));
return;
}
context.drawImage(image, originPoint.x, originPoint.y, image.width, image.height, originPoint.x, originPoint.y, canvas.width, canvas.height);
resolve(getCanvasImageData(context, canvas, offset));
};
});
image.src = src;
return p;
}
function getTextData(textOptions, offset, fill, canvasSettings) {
const canvas = safeDocument().createElement("canvas"), context = canvas.getContext("2d", canvasSettings), { font, text, lines: linesOptions, color } = textOptions;
if (!text || !context) {
return;
}
const lines = text.split(linesOptions.separator), fontSize = isNumber(font.size) ? `${font.size.toString()}px` : font.size, linesData = [];
let maxWidth = 0, totalHeight = 0;
for (const line of lines) {
context.font = `${font.style || ""} ${font.variant || ""} ${font.weight || ""} ${fontSize} ${font.family}`;
const measure = context.measureText(line), lineData = {
measure,
text: line,
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
width: measure.width,
};
maxWidth = Math.max(maxWidth || defaultWidth$3, lineData.width);
totalHeight += lineData.height + linesOptions.spacing;
linesData.push(lineData);
}
canvas.width = maxWidth;
canvas.height = totalHeight;
let currentHeight = 0;
for (const line of linesData) {
context.font = `${font.style || ""} ${font.variant || ""} ${font.weight || ""} ${fontSize} ${font.family}`;
if (fill) {
context.fillStyle = color;
context.fillText(line.text, originPoint.x, currentHeight + line.measure.actualBoundingBoxAscent);
}
else {
context.strokeStyle = color;
context.strokeText(line.text, originPoint.x, currentHeight + line.measure.actualBoundingBoxAscent);
}
currentHeight += line.height + linesOptions.spacing;
}
return getCanvasImageData(context, canvas, offset);
}
async function loadFont(font, weight) {
try {
await safeDocument().fonts.load(`${weight ?? "400"} 36px '${font ?? "Verdana"}'`);
}
catch {
}
}
const maxRetries = 100;
class EmittersCanvasShape extends EmitterShapeBase {
filter;
pixelData;
scale;
#container;
constructor(container, position, size, fill, options) {
super(position, size, fill, options);
this.#container = container;
const filter = options.filter, minAlpha = 0;
let filterFunc = (pixel) => pixel.a > minAlpha;
if (isString(filter)) {
if (filter in globalThis) {
const wndFilter = globalThis[filter];
if (isFunction(wndFilter)) {
filterFunc = wndFilter;
}
}
}
else {
filterFunc = filter;
}
this.filter = filterFunc;
this.scale = options.scale;
this.pixelData = {
pixels: [],
height: 0,
width: 0,
};
}
async init() {
let pixelData;
const options = this.options, selector = options.selector, pixels = options.pixels, image = options.image, element = options.element, text = options.text, offset = pixels.offset;
if (image) {
const url = image.src;
if (!url) {
return;
}
pixelData = await getImageData(url, offset, this.#container.canvas.render.settings);
}
else if (element ?? selector) {
const canvas = element ?? (selector && safeDocument().querySelector(selector));
if (!canvas) {
return;
}
const context = canvas.getContext("2d", this.#container.canvas.render.settings);
if (!context) {
return;
}
pixelData = getCanvasImageData(context, canvas, offset);
}
else {
const data = getTextData(text, offset, this.fill, this.#container.canvas.render.settings);
if (isNull(data)) {
return;
}
pixelData = data;
}
this.pixelData = pixelData;
}
randomPosition() {
const { height, width } = this.pixelData, data = this.pixelData, position = this.position, scale = this.scale, positionOffset = {
x: position.x - width * scale * half$2,
y: position.y - height * scale * half$2,
};
for (let i = 0; i < maxRetries; i++) {
const nextIndex = Math.floor(getRandom() * width * height), pixelPos = {
x: nextIndex % width,
y: Math.floor(nextIndex / width),
}, row = data.pixels[pixelPos.y];
if (!row) {
continue;
}
const pixel = row[pixelPos.x];
if (!pixel) {
continue;
}
const shouldCreateParticle = this.filter(pixel);
if (!shouldCreateParticle) {
continue;
}
return {
position: {
x: pixelPos.x * scale + positionOffset.x,
y: pixelPos.y * scale + positionOffset.y,
},
color: { ...pixel },
opacity: pixel.a,
};
}
return null;
}
resize(position, size) {
super.resize(position, size);
}
}
class PixelsOptions {
offset = 4;
load(data) {
if (isNull(data)) {
return;
}
if (data.offset !== undefined) {
this.offset = data.offset;
}
}
}
class TextFontOptions {
family = "Verdana";
size = 32;
style = "";
variant = "";
weight = "";
load(data) {
if (isNull(data)) {
return;
}
if (data.family !== undefined) {
this.family = data.family;
}
if (data.size !== undefined) {
this.size = data.size;
}
if (data.style !== undefined) {
this.style = data.style;
}
if (data.variant !== undefined) {
this.variant = data.variant;
}
if (data.weight !== undefined) {
this.weight = data.weight;
}
}
}
class TextLinesOptions {
separator = "\n";
spacing = 0;
load(data) {
if (isNull(data)) {
return;
}
if (data.separator !== undefined) {
this.separator = data.separator;
}
if (data.spacing !== undefined) {
this.spacing = data.spacing;
}
}
}
class TextOptions {
color = "#000000";
font = new TextFontOptions();
lines = new TextLinesOptions();
text = "";
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = data.color;
}
this.font.load(data.font);
this.lines.load(data.lines);
if (data.text !== undefined) {
this.text = data.text;
}
}
}
const minAlpha = 0;
class EmittersCanvasShapeOptions {
element;
image;
pixels = new PixelsOptions();
scale = 1;
selector = "";
text = new TextOptions();
filter = (pixel) => pixel.a > minAlpha;
load(data) {
if (isNull(data)) {
return;
}
if (data.element !== undefined) {
this.element = data.element;
}
if (data.filter !== undefined) {
this.filter = data.filter;
}
this.pixels.load(data.pixels);
if (data.scale !== undefined) {
this.scale = data.scale;
}
if (data.selector !== undefined) {
this.selector = data.selector;
}
if (data.image !== undefined) {
this.image = data.image;
}
this.text.load(data.text);
}
}
class EmittersCanvasShapeGenerator {
generate(container, position, size, fill, options) {
const shapeOptions = new EmittersCanvasShapeOptions();
shapeOptions.load(options);
return new EmittersCanvasShape(container, position, size, fill, shapeOptions);
}
}
async function loadEmittersShapeCanvas(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("canvas", new EmittersCanvasShapeGenerator());
});
}
const maxAttempts$1 = 100;
function generateRandomPointWithinPath(ctx, path, center, size) {
let randomPoint = null;
for (let attempts = 0; attempts < maxAttempts$1; attempts++) {
const tmpPoint = {
x: center.x + getRandom() * size.width - size.width * half$2,
y: center.y + getRandom() * size.height - size.height * half$2,
};
if (ctx.isPointInPath(path, tmpPoint.x, tmpPoint.y)) {
randomPoint = tmpPoint;
break;
}
}
return randomPoint;
}
function generateRandomPointOnPathPerimeter(ctx, path, center, size) {
let randomPoint = null;
for (let attempts = 0; attempts < maxAttempts$1; attempts++) {
const tmpPoint = {
x: center.x + getRandom() * size.width - size.width * half$2,
y: center.y + getRandom() * size.height - size.height * half$2,
};
if (ctx.isPointInStroke(path, tmpPoint.x, tmpPoint.y)) {
randomPoint = tmpPoint;
break;
}
}
return randomPoint;
}
class EmittersPathShape extends EmitterShapeBase {
checkContext;
path;
points;
constructor(container, position, size, fill, options) {
super(position, size, fill, options);
const ctx = safeDocument().createElement("canvas").getContext("2d", container.canvas.render.settings);
if (!ctx) {
throw new Error(`No 2d context available`);
}
this.checkContext = ctx;
this.points = options.points;
const pathData = this.points, path = new Path2D(), offset = {
x: position.x - size.width * half$2,
y: position.y - size.height * half$2,
};
for (const [index, point] of pathData.entries()) {
const coords = {
x: offset.x + (point.x * size.width) / percentDenominator,
y: offset.y + (point.y * size.height) / percentDenominator,
};
if (!index) {
path.moveTo(coords.x, coords.y);
}
else {
path.lineTo(coords.x, coords.y);
}
}
const firstIndex = 0, firstPathData = pathData[firstIndex];
if (!firstPathData) {
throw new Error(`No path data available`);
}
const coords = {
x: offset.x + (firstPathData.x * size.width) / percentDenominator,
y: offset.y + (firstPathData.y * size.height) / percentDenominator,
};
path.lineTo(coords.x, coords.y);
this.path = path;
}
async init() {
}
randomPosition() {
const ctx = this.checkContext, position = this.position, size = this.size, fill = this.fill, path = this.path, res = fill
? generateRandomPointWithinPath(ctx, path, position, size)
: generateRandomPointOnPathPerimeter(ctx, path, position, size);
return res ? { position: res } : null;
}
resize(position, size) {
super.resize(position, size);
const pathData = this.points, path = new Path2D(), offset = {
x: position.x - size.width * half$2,
y: position.y - size.height * half$2,
};
for (const [index, point] of pathData.entries()) {
const coords = {
x: offset.x + (point.x * size.width) / percentDenominator,
y: offset.y + (point.y * size.height) / percentDenominator,
};
if (!index) {
path.moveTo(coords.x, coords.y);
}
else {
path.lineTo(coords.x, coords.y);
}
}
const firstIndex = 0, firstPathData = pathData[firstIndex];
if (!firstPathData) {
throw new Error(`No path data available`);
}
const coords = {
x: offset.x + (firstPathData.x * size.width) / percentDenominator,
y: offset.y + (firstPathData.y * size.height) / percentDenominator,
};
path.lineTo(coords.x, coords.y);
this.path = path;
}
}
const defaultPosition = { x: 50, y: 50 };
class EmittersPathShapeOptions {
points = [];
load(data) {
if (isNull(data)) {
return;
}
if (data.points !== undefined) {
this.points = data.points.map(t => ({ x: t.x ?? defaultPosition.x, y: t.y ?? defaultPosition.y }));
}
}
}
class EmittersPathShapeGenerator {
generate(container, position, size, fill, options) {
const shapeOptions = new EmittersPathShapeOptions();
shapeOptions.load(options);
return new EmittersPathShape(container, position, size, fill, shapeOptions);
}
}
async function loadEmittersShapePath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("path", new EmittersPathShapeGenerator());
});
}
const defaultRotation$2 = 0, maxAttempts = 100;
function generateRandomPolygon(position, sides, radius, rotationAngle = defaultRotation$2) {
const polygon = [], angle = doublePI / sides;
for (let i = 0; i < sides; i++) {
const currentAngle = angle * i + rotationAngle;
polygon.push({
x: position.x + radius * Math.cos(currentAngle),
y: position.y + radius * Math.sin(currentAngle),
});
}
return polygon;
}
function generateRandomPointWithinPolygon(polygon) {
const firstIndex = 0, firstPoint = polygon[firstIndex];
if (!firstPoint) {
return null;
}
const min = { ...firstPoint }, max = { ...firstPoint };
for (const point of polygon) {
if (point.x < min.x) {
min.x = point.x;
}
if (point.x > max.x) {
max.x = point.x;
}
if (point.y < min.y) {
min.y = point.y;
}
if (point.y > max.y) {
max.y = point.y;
}
}
let randomPoint = null;
for (let attempts = 0; attempts < maxAttempts; attempts++) {
const tmpPoint = {
x: min.x + getRandom() * (max.x - min.x),
y: min.y + getRandom() * (max.y - min.y),
};
if (isPointInPolygon(tmpPoint, polygon)) {
randomPoint = tmpPoint;
break;
}
}
return randomPoint;
}
function generateRandomPointOnPolygonPerimeter(polygon) {
const sideIndex = Math.floor(getRandom() * polygon.length), startPoint = polygon[sideIndex];
if (!startPoint) {
return;
}
const offset = 1, endPoint = polygon[(sideIndex + offset) % polygon.length];
if (!endPoint) {
return;
}
const t = getRandom();
return { x: startPoint.x + (endPoint.x - startPoint.x) * t, y: startPoint.y + (endPoint.y - startPoint.y) * t };
}
function isPointInPolygon(point, polygon) {
let inside = false;
const offset = 1;
for (let i = 0, j = polygon.length - offset; i < polygon.length; j = i++) {
const pi = polygon[i], pj = polygon[j];
if (!pi || !pj) {
continue;
}
const intersect = pi.y > point.y !== pj.y > point.y && point.x < ((pj.x - pi.x) * (point.y - pi.y)) / (pj.y - pi.y) + pi.x;
if (intersect) {
inside = !inside;
}
}
return inside;
}
class EmittersPolygonShape extends EmitterShapeBase {
angle;
polygon;
sides;
constructor(position, size, fill, options) {
super(position, size, fill, options);
this.sides = options.sides;
this.angle = degToRad(options.angle);
this.polygon = generateRandomPolygon(position, this.sides, size.width * half$2, this.angle);
}
async init() {
}
randomPosition() {
const fill = this.fill, polygon = this.polygon, res = fill ? generateRandomPointWithinPolygon(polygon) : generateRandomPointOnPolygonPerimeter(polygon);
return res ? { position: res } : null;
}
resize(position, size) {
super.resize(position, size);
this.polygon = generateRandomPolygon(position, this.sides, size.width * half$2, this.angle);
}
}
class EmittersPolygonShapeOptions {
angle = 0;
sides = 5;
load(data) {
if (isNull(data)) {
return;
}
if (data.angle !== undefined) {
this.angle = data.angle;
}
if (data.sides !== undefined) {
this.sides = data.sides;
}
}
}
class EmittersPolygonShapeGenerator {
generate(_container, position, size, fill, options) {
const shapeOptions = new EmittersPolygonShapeOptions();
shapeOptions.load(options);
return new EmittersPolygonShape(position, size, fill, shapeOptions);
}
}
async function loadEmittersShapePolygon(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("polygon", new EmittersPolygonShapeGenerator());
});
}
class ExportImagePlugin {
id = "export-image";
async getPlugin(container) {
const { ExportImagePluginInstance } = await Promise.resolve().then(function () { return ExportImagePluginInstance$1; });
return new ExportImagePluginInstance(container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadExportImagePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ExportImagePlugin());
});
}
class ExportJSONPlugin {
id = "export-json";
async getPlugin(container) {
const { ExportJSONPluginInstance } = await Promise.resolve().then(function () { return ExportJSONPluginInstance$1; });
return new ExportJSONPluginInstance(container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadExportJSONPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ExportJSONPlugin());
});
}
class ExportVideoPlugin {
id = "export-video";
async getPlugin(container) {
const { ExportVideoPluginInstance } = await Promise.resolve().then(function () { return ExportVideoPluginInstance$1; });
return new ExportVideoPluginInstance(container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadExportVideoPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ExportVideoPlugin());
});
}
class Cannon {
drawVector = true;
maxDragDistance = 0;
maxParticles = 200;
minParticles = 5;
particleFactor = 0.2;
spread = 30;
vectorColor = "#ffffff80";
velocityFactor = 0.5;
load(data) {
if (!data) {
return;
}
loadProperty(this, "spread", data.spread);
loadProperty(this, "velocityFactor", data.velocityFactor);
loadProperty(this, "particleFactor", data.particleFactor);
loadProperty(this, "minParticles", data.minParticles);
loadProperty(this, "maxParticles", data.maxParticles);
loadProperty(this, "maxDragDistance", data.maxDragDistance);
loadProperty(this, "drawVector", data.drawVector);
loadProperty(this, "vectorColor", data.vectorColor);
}
}
var CannonState;
(function (CannonState) {
CannonState[CannonState["idle"] = 0] = "idle";
CannonState[CannonState["aiming"] = 1] = "aiming";
CannonState[CannonState["fired"] = 2] = "fired";
})(CannonState || (CannonState = {}));
const cannonMode = "cannon", minAngle$3 = 0, powerRadiusMin = 4, powerRadiusMaxDenominator = 20, powerRadiusMaxFactor = identity$3 / powerRadiusMaxDenominator, minTapsLength = 2, piDeg = 180, quarter = 0.25, minDistance$6 = 0;
function angleRad(x0, y0, x1, y1) {
return Math.atan2(y1 - y0, x1 - x0);
}
class Cannoner extends ExternalInteractorBase {
maxDistance = 0;
#data;
#gesture = {
origin: Vector.origin,
current: Vector.origin,
active: false,
};
#lastDownPosition = undefined;
#state = CannonState.idle;
constructor(container) {
super(container);
}
clear(_particle, _delta) {
}
init() {
const options = this.container.actualOptions.interactivity?.modes.cannon ?? new Cannon();
this.#data = {
spread: degToRad(options.spread),
maxDragDistance: options.maxDragDistance,
velocityFactor: options.velocityFactor,
particleFactor: options.particleFactor,
minParticles: options.minParticles,
maxParticles: options.maxParticles,
drawVector: options.drawVector,
vectorColor: options.vectorColor,
};
}
interact(interactivityData, _delta) {
const mouse = interactivityData.mouse, mousePos = mouse.position, clicking = mouse.clicking, downPos = mouse.downPosition;
if (clicking && downPos && downPos !== this.#lastDownPosition && this.#state === CannonState.idle) {
this.#lastDownPosition = downPos;
this.#gesture = {
origin: { x: downPos.x, y: downPos.y },
current: { x: downPos.x, y: downPos.y },
active: true,
};
this.#state = CannonState.aiming;
}
if (this.#state === CannonState.aiming && mousePos) {
this.#gesture.current = { x: mousePos.x, y: mousePos.y };
this.#drawVector();
}
if (!clicking && this.#state === CannonState.aiming) {
this.#gesture.active = false;
this.#fire();
this.#state = CannonState.idle;
}
}
isEnabled(interactivityData) {
const container = this.container, events = container.actualOptions.interactivity?.events;
if (!events?.onClick.enable) {
return false;
}
const modes = events.onClick.mode, modeEnabled = Array.isArray(modes) ? modes.includes(cannonMode) : modes === cannonMode;
if (!modeEnabled) {
return false;
}
return this.#state !== CannonState.idle || interactivityData.mouse.clicking;
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "cannon", Cannon, ...sources);
}
reset(_interactivityData, _particle) {
}
#drawVector() {
this.container.canvas.render.draw(ctx => {
const opts = this.#data;
if (!opts) {
return;
}
const { origin, current } = this.#gesture, pxRatio = this.container.retina.pixelRatio, dragDist = getDistance(origin, current), clampedDist = opts.maxDragDistance > none ? Math.min(dragDist, opts.maxDragDistance * pxRatio) : dragDist, clampRatio = dragDist > minDistance$6 ? clampedDist / dragDist : minDistance$6, clampedX = origin.x + (current.x - origin.x) * clampRatio, clampedY = origin.y + (current.y - origin.y) * clampRatio;
ctx.save();
ctx.strokeStyle = opts.vectorColor;
ctx.lineWidth = double * pxRatio;
ctx.beginPath();
ctx.moveTo(origin.x, origin.y);
ctx.lineTo(clampedX, clampedY);
ctx.stroke();
const radius = Math.max(powerRadiusMin, clampedDist * powerRadiusMaxFactor) * pxRatio;
ctx.beginPath();
ctx.arc(origin.x, origin.y, radius, minAngle$3, doublePI);
ctx.strokeStyle = opts.vectorColor;
ctx.lineWidth = double * pxRatio;
ctx.stroke();
ctx.restore();
});
}
#fire() {
const opts = this.#data;
if (!opts) {
return;
}
const { origin, current } = this.#gesture, pxRatio = this.container.retina.pixelRatio, dist = getDistance(origin, current), dragLength = opts.maxDragDistance > none ? Math.min(dist, opts.maxDragDistance * pxRatio) : dist;
if (dragLength < minTapsLength) {
return;
}
const pxRatioFactor = identity$3 / pxRatio, launchAngle = angleRad(current.x, current.y, origin.x, origin.y), velocity = dragLength * pxRatioFactor * opts.velocityFactor, count = Math.min(opts.maxParticles, Math.max(opts.minParticles, Math.round(dragLength * opts.particleFactor))), toDeg = piDeg / Math.PI;
for (let i = 0; i < count; i++) {
const spreadAngle = launchAngle + getRandomInRange(-opts.spread, opts.spread), speed = getRandomInRange(velocity * quarter, velocity);
this.container.particles.addParticle(origin, {
move: {
enable: true,
speed,
direction: spreadAngle * toDeg,
},
});
}
}
}
async function loadExternalCannonInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalCannon", container => {
return Promise.resolve(new Cannoner(container));
});
});
}
class InteractivityParticleOptions {
options;
pauseOnStop = false;
replaceCursor = false;
stopDelay = 0;
load(data) {
if (isNull(data)) {
return;
}
if (data.options !== undefined) {
this.options = deepExtend({}, data.options);
}
loadProperty(this, "replaceCursor", data.replaceCursor);
loadProperty(this, "pauseOnStop", data.pauseOnStop);
loadProperty(this, "stopDelay", data.stopDelay);
}
}
const particleMode = "particle";
class InteractivityParticleMaker extends ExternalInteractorBase {
maxDistance = 0;
#clearTimeout;
#lastPosition;
#particle;
constructor(container) {
super(container);
}
clear() {
}
init() {
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions;
if (!container.retina.reduceFactor) {
return;
}
const mousePos = interactivityData.mouse.position, interactivityParticleOptions = options.interactivity?.modes.particle;
if (!interactivityParticleOptions) {
return;
}
const mouseStopped = interactivityParticleOptions.pauseOnStop &&
(interactivityData.mouse.position === this.#lastPosition ||
(interactivityData.mouse.position?.x === this.#lastPosition?.x &&
interactivityData.mouse.position?.y === this.#lastPosition?.y)), clearDelay = interactivityParticleOptions.stopDelay;
if (mousePos) {
this.#lastPosition = { ...mousePos };
}
else {
this.#lastPosition = undefined;
}
if (!this.#lastPosition) {
return;
}
if (mouseStopped) {
if (this.#clearTimeout) {
return;
}
this.#clearTimeout = setTimeout(() => {
if (!this.#particle) {
return;
}
if (interactivityParticleOptions.replaceCursor) {
const element = interactivityData.element;
if (element) {
if (element instanceof Window || element instanceof Document) {
safeDocument().body.style.cursor = "";
}
else {
element.style.cursor = "";
}
}
}
this.#particle.destroy(true);
this.#particle = undefined;
}, clearDelay);
return;
}
if (this.#clearTimeout) {
clearTimeout(this.#clearTimeout);
this.#clearTimeout = undefined;
}
if (!this.#particle) {
const particleOptions = deepExtend(interactivityParticleOptions.options, {
move: {
enable: false,
},
});
this.#particle = container.particles.addParticle(this.#lastPosition, particleOptions);
if (interactivityParticleOptions.replaceCursor) {
const element = interactivityData.element;
if (element) {
if (element instanceof Window || element instanceof Document) {
safeDocument().body.style.cursor = "none";
}
else {
element.style.cursor = "none";
}
}
}
}
if (!this.#particle) {
return;
}
this.#particle.position.x = this.#lastPosition.x;
this.#particle.position.y = this.#lastPosition.y;
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
return (!!events &&
((mouse.clicking && mouse.inside && !!mouse.position && isInArray(particleMode, events.onClick.mode)) ||
(mouse.inside && !!mouse.position && isInArray(particleMode, events.onHover.mode))));
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "particle", InteractivityParticleOptions, ...sources);
}
reset() {
}
}
async function loadExternalParticleInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalParticle", container => {
return Promise.resolve(new InteractivityParticleMaker(container));
});
});
}
const popMode = "pop";
class Popper extends ExternalInteractorBase {
handleClickMode;
maxDistance = 0;
constructor(container) {
super(container);
this.handleClickMode = (mode, interactivityData) => {
const container = this.container;
if (mode !== popMode) {
return;
}
const clickPos = interactivityData.mouse.clickPosition;
if (!clickPos) {
return;
}
const poppedParticles = container.particles.grid.queryCircle(clickPos, container.retina.pixelRatio);
if (!poppedParticles.length) {
return;
}
for (const particle of poppedParticles) {
particle.destroy();
}
};
}
clear() {
}
init() {
}
interact() {
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
return !!events && mouse.clicking && mouse.inside && !!mouse.position && isInArray(popMode, events.onClick.mode);
}
reset() {
}
}
async function loadExternalPopInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalPop", container => {
return Promise.resolve(new Popper(container));
});
});
}
class FilterDrawer {
drawAfter(data) {
const { context } = data;
context.restore();
}
drawBefore(data) {
const { context, particle } = data;
context.save();
const blurValue = typeof particle.filterBlur === "number" ? `${particle.filterBlur}px` : particle.filterBlur, brightnessValue = particle.filterBrightness, contrastValue = particle.filterContrast, dropShadowValue = particle.filterDropShadow, grayscaleValue = particle.filterGrayscale, hueRotateValue = typeof particle.filterHueRotate === "number" ? `${particle.filterHueRotate}deg` : particle.filterHueRotate, invertValue = particle.filterInvert, opacityValue = particle.filterOpacity, saturateValue = particle.filterSaturate, sepiaValue = particle.filterSepia, urlValue = particle.filterUrl, blurString = blurValue ? `blur(${blurValue})` : "", brightnessString = isNull(brightnessValue) ? "" : `brightness(${brightnessValue})`, contrastString = isNull(contrastValue) ? "" : `contrast(${contrastValue})`, dropShadowString = dropShadowValue ? `drop-shadow(${dropShadowValue})` : "", grayscaleString = isNull(grayscaleValue) ? "" : `grayscale(${grayscaleValue})`, hueRotateString = isNull(hueRotateValue) ? "" : `hue-rotate(${hueRotateValue})`, invertString = isNull(invertValue) ? "" : `invert(${invertValue})`, opacityString = isNull(opacityValue) ? "" : `opacity(${opacityValue})`, saturateString = isNull(saturateValue) ? "" : `saturate(${saturateValue})`, sepiaString = isNull(sepiaValue) ? "" : `sepia(${sepiaValue})`, urlString = urlValue ? `url(${urlValue})` : "";
context.filter =
`${blurString} ${brightnessString} ${contrastString} ${dropShadowString} ${grayscaleString} ${hueRotateString} ${invertString} ${opacityString} ${saturateString} ${sepiaString} ${urlString}`.trim();
}
particleInit(_container, particle) {
const effectData = particle.effectData;
if (!effectData) {
return;
}
particle.filterBlur = effectData.blur;
particle.filterBrightness = effectData.brightness;
particle.filterContrast = effectData.contrast;
particle.filterDropShadow = effectData.dropShadow;
particle.filterGrayscale = effectData.grayscale;
particle.filterHueRotate = effectData.hueRotate;
particle.filterInvert = effectData.invert;
particle.filterOpacity = effectData.opacity;
particle.filterSaturate = effectData.saturate;
particle.filterSepia = effectData.sepia;
particle.filterUrl = effectData.url;
}
}
async function loadFilterEffect(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addEffect("filter", () => {
return Promise.resolve(new FilterDrawer());
});
});
}
const lerp = (a, b, t) => {
return a + t * (b - a);
}, smoothstep = (t) => {
return t * t * (3 - 2 * t);
}, fract = (x) => {
return x - Math.floor(x);
};
class SmoothValueNoise {
#coeffW = 29.537;
#coeffX = 127.1;
#coeffY = 311.7;
#coeffZ = 78.233;
#coeffZW = 37.719;
#scaleFactor = 43758.5453;
noise2d(x, y) {
const x0 = Math.floor(x), y0 = Math.floor(y), x1 = x0 + 1, y1 = y0 + 1, sx = smoothstep(x - x0), sy = smoothstep(y - y0), n00 = this.#valueNoise2D(x0, y0), n10 = this.#valueNoise2D(x1, y0), n01 = this.#valueNoise2D(x0, y1), n11 = this.#valueNoise2D(x1, y1), ix0 = lerp(n00, n10, sx), ix1 = lerp(n01, n11, sx);
return lerp(ix0, ix1, sy);
}
noise3d(x, y, z) {
const x0 = Math.floor(x), y0 = Math.floor(y), z0 = Math.floor(z), x1 = x0 + 1, y1 = y0 + 1, z1 = z0 + 1, sx = smoothstep(x - x0), sy = smoothstep(y - y0), sz = smoothstep(z - z0), n000 = this.#valueNoise3D(x0, y0, z0), n100 = this.#valueNoise3D(x1, y0, z0), n010 = this.#valueNoise3D(x0, y1, z0), n110 = this.#valueNoise3D(x1, y1, z0), n001 = this.#valueNoise3D(x0, y0, z1), n101 = this.#valueNoise3D(x1, y0, z1), n011 = this.#valueNoise3D(x0, y1, z1), n111 = this.#valueNoise3D(x1, y1, z1), ix00 = lerp(n000, n100, sx), ix10 = lerp(n010, n110, sx), ix01 = lerp(n001, n101, sx), ix11 = lerp(n011, n111, sx), iy0 = lerp(ix00, ix10, sy), iy1 = lerp(ix01, ix11, sy);
return lerp(iy0, iy1, sz);
}
noise4d(x, y, z, w) {
const x0 = Math.floor(x), y0 = Math.floor(y), z0 = Math.floor(z), w0 = Math.floor(w), x1 = x0 + 1, y1 = y0 + 1, z1 = z0 + 1, w1 = w0 + 1, sx = smoothstep(x - x0), sy = smoothstep(y - y0), sz = smoothstep(z - z0), sw = smoothstep(w - w0), n0000 = this.#valueNoise4D(x0, y0, z0, w0), n1000 = this.#valueNoise4D(x1, y0, z0, w0), n0100 = this.#valueNoise4D(x0, y1, z0, w0), n1100 = this.#valueNoise4D(x1, y1, z0, w0), n0010 = this.#valueNoise4D(x0, y0, z1, w0), n1010 = this.#valueNoise4D(x1, y0, z1, w0), n0110 = this.#valueNoise4D(x0, y1, z1, w0), n1110 = this.#valueNoise4D(x1, y1, z1, w0), n0001 = this.#valueNoise4D(x0, y0, z0, w1), n1001 = this.#valueNoise4D(x1, y0, z0, w1), n0101 = this.#valueNoise4D(x0, y1, z0, w1), n1101 = this.#valueNoise4D(x1, y1, z0, w1), n0011 = this.#valueNoise4D(x0, y0, z1, w1), n1011 = this.#valueNoise4D(x1, y0, z1, w1), n0111 = this.#valueNoise4D(x0, y1, z1, w1), n1111 = this.#valueNoise4D(x1, y1, z1, w1), ix000 = lerp(n0000, n1000, sx), ix100 = lerp(n0100, n1100, sx), ix010 = lerp(n0010, n1010, sx), ix110 = lerp(n0110, n1110, sx), ix001 = lerp(n0001, n1001, sx), ix101 = lerp(n0101, n1101, sx), ix011 = lerp(n0011, n1011, sx), ix111 = lerp(n0111, n1111, sx), iy00 = lerp(ix000, ix100, sy), iy10 = lerp(ix010, ix110, sy), iy01 = lerp(ix001, ix101, sy), iy11 = lerp(ix011, ix111, sy), iz0 = lerp(iy00, iy10, sz), iz1 = lerp(iy01, iy11, sz);
return lerp(iz0, iz1, sw);
}
seed(seed) {
const s = Math.sin(seed) * 10000;
this.#coeffX = fract(s * 15731);
this.#coeffY = fract(s * 789221);
this.#coeffZ = fract(s * 1376312589);
this.#coeffZW = fract(s * 974634777);
this.#coeffW = fract(s * 592558533);
this.#scaleFactor = 43758.5453;
}
#valueNoise2D(x, y) {
const n = Math.sin(x * this.#coeffX + y * this.#coeffY) * this.#scaleFactor;
return (n - Math.floor(n)) * 2 - 1;
}
#valueNoise3D(x, y, z) {
const n = Math.sin(x * this.#coeffX + y * this.#coeffY + z * this.#coeffZ) * this.#scaleFactor;
return (n - Math.floor(n)) * 2 - 1;
}
#valueNoise4D(x, y, z, w) {
const n = Math.sin(x * this.#coeffX + y * this.#coeffY + z * this.#coeffZW + w * this.#coeffW) * this.#scaleFactor;
return (n - Math.floor(n)) * 2 - 1;
}
}
class FractalNoise {
#smoothValueNoise;
constructor() {
this.#smoothValueNoise = new SmoothValueNoise();
}
noise2d(x, y, octaves = 5, persistence = 0.5, lacunarity = 2) {
let total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for (let i = 0; i < octaves; i++) {
total += this.#smoothValueNoise.noise2d(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return total / maxValue;
}
noise3d(x, y, z, octaves = 5, persistence = 0.5, lacunarity = 2) {
let total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for (let i = 0; i < octaves; i++) {
total += this.#smoothValueNoise.noise3d(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return total / maxValue;
}
noise4d(x, y, z, w, octaves = 5, persistence = 0.5, lacunarity = 2) {
let total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for (let i = 0; i < octaves; i++) {
total += this.#smoothValueNoise.noise4d(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return total / maxValue;
}
seed(seed) {
this.#smoothValueNoise.seed(seed);
}
}
const originCoordinate = 0, firstIndex$4 = 0, empty = 0, optionsSizeOffset = 1, transformDefaultValues = {
a: 1,
b: 0,
c: 0,
d: 1,
e: 0,
f: 0,
}, defaultOptions$3 = {
draw: false,
size: 20,
increment: 0.004,
columns: 0,
rows: 0,
layers: 0,
width: 0,
height: 0,
factor: {
angle: 0.02,
length: 0.01,
},
offset: {
x: 40000,
y: 40000,
z: 40000,
},
};
class NoiseFieldGenerator {
container;
field;
noiseGen;
noiseW;
options;
#res;
constructor(container, noiseGen) {
this.container = container;
this.noiseGen = noiseGen;
this.field = [];
this.noiseW = 0;
this.#res = Vector.origin;
this.options = deepExtend({}, defaultOptions$3);
}
generate(particle) {
const pos = particle.getPosition(), { size } = this.options, sizeFactor = identity$3 / size, point = {
x: Math.max(Math.floor(pos.x * sizeFactor), originCoordinate),
y: Math.max(Math.floor(pos.y * sizeFactor), originCoordinate),
z: Math.max(Math.floor(pos.z * sizeFactor), originCoordinate),
}, { field } = this, fieldPoint = field[point.x]?.[point.y]?.[point.z];
if (fieldPoint) {
this.#res.x = fieldPoint.x;
this.#res.y = fieldPoint.y;
}
else {
this.#res.x = 0;
this.#res.y = 0;
}
return this.#res;
}
init() {
this.#setup();
}
reset() {
}
update() {
this.#calculateField();
this.noiseW += this.options.increment;
if (!this.options.draw) {
return;
}
this.container.canvas.render.draw(ctx => {
this.#drawField(ctx);
});
}
#calculateField() {
const field = this.field, noiseGen = this.noiseGen, options = this.options, noiseW = this.noiseW, lengthFactor = options.factor.length, angleFactor = options.factor.angle;
for (let x = 0; x < options.columns; x++) {
const xColumn = field[x];
if (!xColumn) {
continue;
}
for (let y = 0; y < options.rows; y++) {
const yColumn = xColumn[y];
if (!yColumn) {
continue;
}
for (let z = 0; z < options.layers; z++) {
const cell = yColumn[z];
if (!cell) {
continue;
}
cell.length = noiseGen.noise4d(x * lengthFactor + options.offset.x, y * lengthFactor + options.offset.y, z * lengthFactor + options.offset.z, noiseW);
cell.angle = noiseGen.noise4d(x * angleFactor, y * angleFactor, z * angleFactor, noiseW) * doublePI;
}
}
}
}
#drawField(ctx) {
const { field, options } = this;
for (let x = 0; x < options.columns; x++) {
const xColumn = field[x];
if (!xColumn) {
continue;
}
for (let y = 0; y < options.rows; y++) {
const yColumn = xColumn[y];
if (!yColumn) {
continue;
}
const cell = yColumn[firstIndex$4];
if (!cell) {
continue;
}
const { angle, length } = cell;
ctx.setTransform(transformDefaultValues.a, transformDefaultValues.b, transformDefaultValues.c, transformDefaultValues.d, x * this.options.size, y * this.options.size);
ctx.rotate(angle);
ctx.strokeStyle = "white";
ctx.beginPath();
ctx.moveTo(originCoordinate, originCoordinate);
ctx.lineTo(originCoordinate, this.options.size * length);
ctx.stroke();
ctx.setTransform(transformDefaultValues.a, transformDefaultValues.b, transformDefaultValues.c, transformDefaultValues.d, transformDefaultValues.e, transformDefaultValues.f);
}
}
}
#initField() {
const { columns, rows, layers } = this.options;
this.field = new Array(columns);
for (let x = 0; x < columns; x++) {
const newX = new Array(rows);
for (let y = 0; y < rows; y++) {
const newY = new Array(layers);
for (let z = 0; z < layers; z++) {
newY[z] = Vector.origin;
}
newX[y] = newY;
}
this.field[x] = newX;
}
}
#resetField() {
const container = this.container, sourceOptions = container.actualOptions.particles.move.path.options, { options } = this;
options.width = container.canvas.size.width;
options.height = container.canvas.size.height;
options.size = sourceOptions["size"] > empty ? sourceOptions["size"] : defaultOptions$3.size;
options.increment =
sourceOptions["increment"] > empty
? sourceOptions["increment"]
: defaultOptions$3.increment;
options.draw = !!sourceOptions["draw"];
const offset = sourceOptions["offset"];
options.offset.x = offset?.x ?? defaultOptions$3.offset.x;
options.offset.y = offset?.y ?? defaultOptions$3.offset.y;
options.offset.z = offset?.z ?? defaultOptions$3.offset.z;
const factor = sourceOptions["factor"];
options.factor.angle = factor?.angle ?? defaultOptions$3.factor.angle;
options.factor.length = factor?.length ?? defaultOptions$3.factor.length;
options.seed = sourceOptions["seed"];
this.noiseGen.seed(options.seed ?? getRandom());
options.columns = Math.floor(options.width / options.size) + optionsSizeOffset;
options.rows = Math.floor(options.height / options.size) + optionsSizeOffset;
options.layers = Math.floor(container.zLayers / options.size) + optionsSizeOffset;
this.#initField();
}
#setup() {
this.noiseW = 0;
this.#resetField();
addEventListener("resize", () => {
this.#resetField();
});
}
}
class FractalNoiseGenerator extends NoiseFieldGenerator {
constructor(container) {
const fractalNoise = new FractalNoise();
super(container, {
noise4d: (x, y, z, w) => fractalNoise.noise4d(x, y, z, w),
seed: seed => {
fractalNoise.seed(seed);
},
});
}
}
const fractalNoisePathName = "fractalNoise";
async function loadFractalNoisePath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(fractalNoisePathName, container => {
return Promise.resolve(new FractalNoiseGenerator(container));
});
});
}
class AbsorberLife {
count;
delay;
duration;
wait = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "count", data.count);
loadRangeProperty(this, "delay", data.delay);
loadRangeProperty(this, "duration", data.duration);
loadProperty(this, "wait", data.wait);
}
}
class AbsorberSizeLimit {
mass = 0;
radius = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "mass", data.mass);
loadProperty(this, "radius", data.radius);
}
}
class AbsorberSize extends ValueWithRandom {
density = 5;
limit = new AbsorberSizeLimit();
value = 50;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "density", data.density);
if (isNumber(data.limit)) {
this.limit.radius = data.limit;
}
else {
this.limit.load(data.limit);
}
}
}
class Absorber {
color;
destroy;
draggable;
life;
name;
opacity;
orbits;
position;
size;
constructor() {
this.color = new OptionsColor();
this.color.value = "#000000";
this.draggable = false;
this.opacity = 1;
this.destroy = true;
this.orbits = false;
this.life = new AbsorberLife();
this.size = new AbsorberSize();
}
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "draggable", data.draggable);
if (data.life !== undefined) {
this.life.load(data.life);
}
this.name = data.name;
loadProperty(this, "opacity", data.opacity);
if (data.position !== undefined) {
this.position = {};
if (data.position.x !== undefined) {
this.position.x = setRangeValue(data.position.x);
}
if (data.position.y !== undefined) {
this.position.y = setRangeValue(data.position.y);
}
}
if (data.size !== undefined) {
this.size.load(data.size);
}
loadProperty(this, "destroy", data.destroy);
loadProperty(this, "orbits", data.orbits);
}
}
const absorbersMode = "absorbers";
class AbsorbersInteractor extends ExternalInteractorBase {
handleClickMode;
maxDistance;
#dragging = false;
#draggingAbsorber;
#instancesManager;
constructor(container, instancesManager) {
super(container);
this.maxDistance = 0;
this.#instancesManager = instancesManager;
this.#instancesManager.initContainer(container);
this.handleClickMode = (mode, interactivityData) => {
const container = this.container, options = container.actualOptions, absorbers = options.interactivity.modes.absorbers;
if (!absorbers || mode !== absorbersMode) {
return;
}
const { clickPosition } = interactivityData.mouse;
if (clickPosition) {
const existingAbsorber = instancesManager
.getArray(this.container)
.some(t => getDistance(t.position, clickPosition) < t.size);
if (existingAbsorber) {
return;
}
}
const absorbersModeOptions = itemFromArray(absorbers) ?? new Absorber();
void this.#instancesManager.addAbsorber(container, absorbersModeOptions, clickPosition);
};
}
clear() {
}
init() {
}
interact(interactivityData, delta) {
for (const particle of this.container.particles.filter(p => this.isEnabled(interactivityData, p))) {
for (const absorber of this.#instancesManager.getArray(this.container)) {
if (absorber.options.draggable) {
const mouse = interactivityData.mouse;
if (mouse.clicking && mouse.downPosition) {
const mouseDist = getDistance(absorber.position, mouse.downPosition);
if (mouseDist <= absorber.size) {
this.#dragging = true;
this.#draggingAbsorber = absorber;
}
}
else {
this.#dragging = false;
this.#draggingAbsorber = undefined;
}
if (this.#dragging && this.#draggingAbsorber == absorber && mouse.position) {
absorber.position.x = mouse.position.x;
absorber.position.y = mouse.position.y;
}
}
absorber.attract(particle, delta);
if (particle.destroyed) {
break;
}
}
}
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity).events;
if (!mouse.clickPosition || !events.onClick.enable) {
return false;
}
return isInArray(absorbersMode, events.onClick.mode);
}
loadModeOptions(options, ...sources) {
options.absorbers ??= [];
for (const source of sources) {
if (!source) {
continue;
}
if (isArray(source.absorbers)) {
for (const absorber of source.absorbers) {
const tmp = new Absorber();
tmp.load(absorber);
options.absorbers.push(tmp);
}
}
else {
const tmp = new Absorber();
tmp.load(source.absorbers);
options.absorbers.push(tmp);
}
}
}
reset() {
}
}
const instancesManagers = new WeakMap();
function getAbsorbersInstancesManager(e) {
const pluginManager = e.pluginManager;
let manager = instancesManagers.get(pluginManager);
if (!manager) {
manager = Promise.resolve().then(function () { return AbsorbersInstancesManager$1; })
.then(({ AbsorbersInstancesManager }) => new AbsorbersInstancesManager(pluginManager))
.catch((error) => {
instancesManagers.delete(pluginManager);
throw error;
});
instancesManagers.set(pluginManager, manager);
}
return manager;
}
async function loadAbsorbersInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const pluginManager = e.pluginManager, instancesManager = await getAbsorbersInstancesManager(e);
ensureInteractivityPluginLoaded(e);
pluginManager.addInteractor?.("externalAbsorbers", container => {
return Promise.resolve(new AbsorbersInteractor(container, instancesManager));
});
});
}
class AbsorbersPlugin {
id = "absorbers";
#instancesManager;
constructor(instancesManager) {
this.#instancesManager = instancesManager;
}
async getPlugin(container) {
const { AbsorbersPluginInstance } = await Promise.resolve().then(function () { return AbsorbersPluginInstance$1; });
return new AbsorbersPluginInstance(container, this.#instancesManager);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
if (source?.absorbers) {
options.absorbers = executeOnSingleOrMultiple(source.absorbers, absorber => {
const tmp = new Absorber();
tmp.load(absorber);
return tmp;
});
}
}
needsPlugin(options) {
if (!options) {
return false;
}
const absorbers = options.absorbers;
if (isArray(absorbers)) {
return !!absorbers.length;
}
else if (absorbers) {
return true;
}
return false;
}
}
async function loadAbsorbersPluginSimple(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const pluginManager = e.pluginManager, instancesManager = await getAbsorbersInstancesManager(e);
pluginManager.addPlugin(new AbsorbersPlugin(instancesManager));
});
}
async function loadAbsorbersPlugin(engine) {
await loadAbsorbersPluginSimple(engine);
await loadAbsorbersInteraction(engine);
}
class DestroyBounds {
bottom;
left;
right;
top;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "bottom", data.bottom);
loadRangeProperty(this, "left", data.left);
loadRangeProperty(this, "right", data.right);
loadRangeProperty(this, "top", data.top);
}
}
var DestroyMode;
(function (DestroyMode) {
DestroyMode["explode"] = "explode";
DestroyMode["none"] = "none";
DestroyMode["split"] = "split";
})(DestroyMode || (DestroyMode = {}));
class Explode {
maxSizeFactor = 3;
speed = 2;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "maxSizeFactor", data.maxSizeFactor);
loadProperty(this, "speed", data.speed);
}
}
class SplitFactor extends ValueWithRandom {
value = 3;
}
class SplitRate extends ValueWithRandom {
value = { min: 4, max: 9 };
}
class Split {
count = 1;
factor = new SplitFactor();
fillColor;
fillColorOffset;
particles;
rate = new SplitRate();
sizeOffset = true;
strokeColor;
strokeColorOffset;
load(data) {
if (isNull(data)) {
return;
}
if (data.fillColor !== undefined) {
this.fillColor = OptionsColor.create(this.fillColor, data.fillColor);
}
if (data.strokeColor !== undefined) {
this.strokeColor = OptionsColor.create(this.strokeColor, data.strokeColor);
}
loadProperty(this, "count", data.count);
this.factor.load(data.factor);
this.rate.load(data.rate);
this.particles = executeOnSingleOrMultiple(data.particles, particles => {
return deepExtend({}, particles);
});
loadProperty(this, "sizeOffset", data.sizeOffset);
if (data.fillColorOffset) {
this.fillColorOffset = this.fillColorOffset ?? {};
if (data.fillColorOffset.h !== undefined) {
this.fillColorOffset.h = data.fillColorOffset.h;
}
if (data.fillColorOffset.s !== undefined) {
this.fillColorOffset.s = data.fillColorOffset.s;
}
if (data.fillColorOffset.l !== undefined) {
this.fillColorOffset.l = data.fillColorOffset.l;
}
}
if (data.strokeColorOffset) {
this.strokeColorOffset = this.strokeColorOffset ?? {};
if (data.strokeColorOffset.h !== undefined) {
this.strokeColorOffset.h = data.strokeColorOffset.h;
}
if (data.strokeColorOffset.s !== undefined) {
this.strokeColorOffset.s = data.strokeColorOffset.s;
}
if (data.strokeColorOffset.l !== undefined) {
this.strokeColorOffset.l = data.strokeColorOffset.l;
}
}
}
}
let Destroy$1 = class Destroy {
bounds = new DestroyBounds();
explode = new Explode();
mode = DestroyMode.none;
split = new Split();
load(data) {
if (isNull(data)) {
return;
}
if (data.mode) {
this.mode = data.mode;
}
if (data.bounds) {
this.bounds.load(data.bounds);
}
this.explode.load(data.explode);
this.split.load(data.split);
}
};
const defaultOffset = 0, minDestroySize = 0.5, defaultSplitCount = 0, increment = 1, unbreakableTime = 500, minSplitCount = 0, minHue = 0, hueRange = 360, minSaturation = 0, maxSaturation = 100, minLightness = 0, maxLightness = 100;
function createParentColor(parentColor) {
return AnimatableColor.create(undefined, {
value: {
hsl: parentColor,
},
});
}
function createOffsetColor(parentColor, offset) {
const offsetH = getRangeValue(offset.h ?? defaultOffset), offsetS = getRangeValue(offset.s ?? defaultOffset), offsetL = getRangeValue(offset.l ?? defaultOffset), h = (parentColor.h + offsetH) % hueRange, s = Math.max(minSaturation, Math.min(maxSaturation, parentColor.s + offsetS)), l = Math.max(minLightness, Math.min(maxLightness, parentColor.l + offsetL));
return AnimatableColor.create(undefined, {
value: {
hsl: {
h: h < minHue ? h + hueRange : h,
s,
l,
},
},
});
}
function resolveSplitColor(offset, splitColor, splitParticlesColor, parentColor) {
if (offset && parentColor) {
return createOffsetColor(parentColor, offset);
}
if (splitColor) {
return AnimatableColor.create(undefined, splitColor);
}
if (splitParticlesColor) {
return AnimatableColor.create(undefined, splitParticlesColor);
}
return parentColor ? createParentColor(parentColor) : undefined;
}
function addSplitParticle(pluginManager, container, parent, splitParticlesOptions) {
const destroyOptions = parent.options.destroy;
if (!destroyOptions) {
return;
}
const splitOptions = destroyOptions.split, splitParticleOptions = loadParticlesOptions(pluginManager, container, parent.options), splitFillColor = itemFromSingleOrMultiple(splitOptions.fillColor), splitStrokeColor = itemFromSingleOrMultiple(splitOptions.strokeColor), parentFillColor = parent.getFillColor(), parentStrokeColor = parent.getStrokeColor();
splitParticleOptions.move.load({
center: {
x: parent.position.x,
y: parent.position.y,
mode: exports.PixelMode.precise,
},
});
const factor = identity$3 / getRangeValue(splitOptions.factor.value), sizeOptions = splitParticleOptions["size"];
if (sizeOptions) {
if (isNumber(sizeOptions.value)) {
sizeOptions.value *= factor;
}
else {
sizeOptions.value.min *= factor;
sizeOptions.value.max *= factor;
}
}
splitParticleOptions.load(splitParticlesOptions);
const splitParticlePaintOptions = itemFromSingleOrMultiple(splitParticleOptions.paint), splitParticleFillOptions = splitParticlePaintOptions?.fill, splitParticleStrokeOptions = splitParticlePaintOptions?.stroke, fillColor = resolveSplitColor(splitOptions.fillColorOffset, splitFillColor, splitParticleFillOptions?.color, parentFillColor), strokeColor = resolveSplitColor(splitOptions.strokeColorOffset, splitStrokeColor, splitParticleStrokeOptions?.color, parentStrokeColor);
if (fillColor && splitParticleFillOptions) {
splitParticleFillOptions.color = fillColor;
}
if (strokeColor && splitParticleStrokeOptions) {
splitParticleStrokeOptions.color = strokeColor;
}
const offset = splitOptions.sizeOffset ? setRangeValue(-parent.size.value, parent.size.value) : defaultOffset, position = {
x: parent.position.x + randomInRangeValue(offset),
y: parent.position.y + randomInRangeValue(offset),
};
return container.particles.addParticle(position, splitParticleOptions, parent.group, (particle) => {
if (particle.size.value < minDestroySize) {
return false;
}
particle.velocity.length = randomInRangeValue(setRangeValue(parent.velocity.length, particle.velocity.length));
particle.splitCount = (parent.splitCount ?? defaultSplitCount) + increment;
particle.unbreakable = true;
particle.unbreakableUntil = performance.now() + unbreakableTime;
return true;
});
}
function split(pluginManager, container, particle) {
const destroyOptions = particle.options.destroy;
if (!destroyOptions) {
return;
}
const splitOptions = destroyOptions.split;
if (splitOptions.count >= minSplitCount) {
if (particle.splitCount === undefined || particle.splitCount > splitOptions.count) {
return;
}
particle.splitCount++;
}
const rate = getRangeValue(splitOptions.rate.value), particlesSplitOptions = itemFromSingleOrMultiple(splitOptions.particles);
for (let i = 0; i < rate; i++) {
addSplitParticle(pluginManager, container, particle, particlesSplitOptions);
}
}
const defaultDeltaFactor$1 = 1, fpsFactor = 60, minExplodeSpeed = 0.01, maxExplodeProgress = 1;
class DestroyUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
}
init(particle) {
const container = this.#container, particlesOptions = particle.options, destroyOptions = particlesOptions.destroy;
if (!destroyOptions) {
return;
}
particle.exploding = undefined;
particle.splitCount = 0;
const destroyBoundsOptions = destroyOptions.bounds;
particle.destroyBounds ??= {};
const { bottom, left, right, top } = destroyBoundsOptions, { destroyBounds } = particle, canvasSize = container.canvas.size;
if (bottom) {
destroyBounds.bottom = (getRangeValue(bottom) * canvasSize.height) / percentDenominator;
}
if (left) {
destroyBounds.left = (getRangeValue(left) * canvasSize.width) / percentDenominator;
}
if (right) {
destroyBounds.right = (getRangeValue(right) * canvasSize.width) / percentDenominator;
}
if (top) {
destroyBounds.top = (getRangeValue(top) * canvasSize.height) / percentDenominator;
}
}
isEnabled(particle) {
const destroyParticle = particle;
return !destroyParticle.destroyed || !!destroyParticle.exploding;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "destroy", Destroy$1, ...sources);
}
particleDestroyed(particle, override) {
if (override) {
return;
}
const destroyOptions = particle.options.destroy;
switch (destroyOptions?.mode) {
case DestroyMode.split:
split(this.#pluginManager, this.#container, particle);
break;
case DestroyMode.explode: {
if (particle.exploding) {
particle.destroyed = false;
break;
}
const { explode } = destroyOptions, initialSize = particle.size.value, maxSize = initialSize * explode.maxSizeFactor, opacity = particle.getOpacity();
particle.exploding = {
initialFillOpacity: opacity.fillOpacity,
initialSize,
initialStrokeOpacity: opacity.strokeOpacity,
maxSize,
progress: 0,
speed: Math.max(explode.speed, minExplodeSpeed),
};
particle.fillOpacity = opacity.fillOpacity;
particle.strokeOpacity = opacity.strokeOpacity;
particle.destroyed = false;
break;
}
}
}
update(particle, delta) {
if (particle.exploding) {
const explosionState = particle.exploding, deltaFactor = delta.factor || defaultDeltaFactor$1;
explosionState.progress = Math.min(maxExplodeProgress, explosionState.progress + (explosionState.speed * deltaFactor) / fpsFactor);
const progress = explosionState.progress;
particle.size.value =
explosionState.initialSize + (explosionState.maxSize - explosionState.initialSize) * progress;
particle.fillOpacity = explosionState.initialFillOpacity * (maxExplodeProgress - progress);
particle.strokeOpacity = explosionState.initialStrokeOpacity * (maxExplodeProgress - progress);
if (progress >= maxExplodeProgress) {
particle.exploding = undefined;
particle.destroy(true);
}
return;
}
if (particle.unbreakableUntil !== undefined && performance.now() >= particle.unbreakableUntil) {
particle.unbreakable = false;
particle.unbreakableUntil = undefined;
}
const position = particle.getPosition(), bounds = particle.destroyBounds;
if (!bounds) {
return;
}
if ((bounds.bottom !== undefined && position.y >= bounds.bottom) ||
(bounds.left !== undefined && position.x <= bounds.left) ||
(bounds.right !== undefined && position.x >= bounds.right) ||
(bounds.top !== undefined && position.y <= bounds.top)) {
particle.destroy();
}
}
}
async function loadDestroyUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("destroy", container => {
return Promise.resolve(new DestroyUpdater(e.pluginManager, container));
});
});
}
class EmittersCircleShape extends EmitterShapeBase {
constructor(position, size, fill, options) {
super(position, size, fill, options);
}
async init() {
}
randomPosition() {
const size = this.size, fill = this.fill, position = this.position, generateTheta = (x, y) => {
const u = getRandom() * quarter$2, theta = Math.atan((y / x) * Math.tan(doublePI * u)), v = getRandom();
if (v < quarter$2) {
return theta;
}
else if (v < double * quarter$2) {
return Math.PI - theta;
}
else if (v < double * quarter$2 + quarter$2) {
return Math.PI + theta;
}
else {
return -theta;
}
}, radius = (x, y, theta) => (x * y) / Math.sqrt((y * Math.cos(theta)) ** squareExp + (x * Math.sin(theta)) ** squareExp), [a, b] = [size.width * half$2, size.height * half$2], randomTheta = generateTheta(a, b), maxRadius = radius(a, b, randomTheta), randomRadius = fill ? maxRadius * Math.sqrt(getRandom()) : maxRadius;
return {
position: {
x: position.x + randomRadius * Math.cos(randomTheta),
y: position.y + randomRadius * Math.sin(randomTheta),
},
};
}
}
class EmittersCircleShapeGenerator {
generate(_container, position, size, fill, options) {
return new EmittersCircleShape(position, size, fill, options);
}
}
async function loadEmittersShapeCircle(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("circle", new EmittersCircleShapeGenerator());
});
}
const sides$5 = 4;
var Sides;
(function (Sides) {
Sides[Sides["TopLeft"] = 0] = "TopLeft";
Sides[Sides["TopRight"] = 1] = "TopRight";
Sides[Sides["BottomRight"] = 2] = "BottomRight";
Sides[Sides["BottomLeft"] = 3] = "BottomLeft";
})(Sides || (Sides = {}));
function randomSquareCoordinate(position, offset) {
return position + offset * (getRandom() - half$2);
}
function getRandomSize(sides) {
return Math.floor(getRandom() * sides);
}
class EmittersSquareShape extends EmitterShapeBase {
constructor(position, size, fill, options) {
super(position, size, fill, options);
}
async init() {
}
randomPosition() {
const fill = this.fill, position = this.position, size = this.size;
if (fill) {
return {
position: {
x: randomSquareCoordinate(position.x, size.width),
y: randomSquareCoordinate(position.y, size.height),
},
};
}
else {
const halfW = size.width * half$2, halfH = size.height * half$2, side = getRandomSize(sides$5), v = (getRandom() - half$2) * double;
switch (side) {
case Sides.TopLeft:
return {
position: {
x: position.x + v * halfW,
y: position.y - halfH,
},
};
case Sides.TopRight:
return {
position: {
x: position.x - halfW,
y: position.y + v * halfH,
},
};
case Sides.BottomRight:
return {
position: {
x: position.x + v * halfW,
y: position.y + halfH,
},
};
case Sides.BottomLeft:
default:
return {
position: {
x: position.x + halfW,
y: position.y + v * halfH,
},
};
}
}
}
}
class EmittersSquareShapeGenerator {
generate(_container, position, size, fill, options) {
return new EmittersSquareShape(position, size, fill, options);
}
}
async function loadEmittersShapeSquare(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("square", new EmittersSquareShapeGenerator());
});
}
const defaultMomentumFactor$1 = 0.03, defaultPreserveMomentum = false;
class Drag {
momentumFactor = defaultMomentumFactor$1;
preserveMomentum = defaultPreserveMomentum;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "momentumFactor", data.momentumFactor);
loadProperty(this, "preserveMomentum", data.preserveMomentum);
}
}
const clickReleaseDelayMs = 50, defaultMomentumFactor = 0.03, dragMode = "drag", firstSampleIndex = 0, lastSampleOffset = 1, maxMomentumSamples = 5, minSamplesForMomentum = 2, momentumToVelocityFactor = millisecondsToSeconds$1 / defaultFps, zeroValue = 0;
class Dragger extends ExternalInteractorBase {
maxDistance = 0;
#dragStartClickTime;
#dragStartClickingFalseTime;
#draggedParticle;
#grabOffset;
#lastMousePosition;
#momentumSamples = [];
#mouseDownHandled = false;
#savedVelocity;
constructor(container) {
super(container);
}
clear(_particle, _delta) {
}
init() {
}
interact(interactivityData, _delta) {
const mouse = interactivityData.mouse, mousePos = mouse.position;
if (mousePos) {
this.#lastMousePosition = mousePos;
}
if (this.#draggedParticle) {
const now = performance.now();
if (!mouse.clicking) {
this.#dragStartClickingFalseTime ??= now;
}
else {
this.#dragStartClickingFalseTime = undefined;
}
const hasStableClickEndWithoutClickTime = this.#dragStartClickTime === undefined &&
this.#dragStartClickingFalseTime !== undefined &&
now - this.#dragStartClickingFalseTime >= clickReleaseDelayMs;
if (!mouse.inside || this.#hasClickEnded(mouse) || hasStableClickEndWithoutClickTime) {
this.#releaseDragged();
this.#mouseDownHandled = false;
return;
}
const pointerPos = mousePos ?? this.#lastMousePosition;
if (!pointerPos) {
return;
}
this.#momentumSamples.push({ x: pointerPos.x, y: pointerPos.y, t: now });
if (this.#momentumSamples.length > maxMomentumSamples) {
this.#momentumSamples.shift();
}
const targetX = pointerPos.x + (this.#grabOffset?.x ?? zeroValue), targetY = pointerPos.y + (this.#grabOffset?.y ?? zeroValue);
this.#draggedParticle.position.x = targetX - this.#draggedParticle.offset.x;
this.#draggedParticle.position.y = targetY - this.#draggedParticle.offset.y;
this.#draggedParticle.velocity.x = zeroValue;
this.#draggedParticle.velocity.y = zeroValue;
this.#draggedParticle.initialPosition.x = this.#draggedParticle.position.x;
this.#draggedParticle.initialPosition.y = this.#draggedParticle.position.y;
this.#draggedParticle.misplaced = false;
return;
}
if (!mouse.clicking) {
this.#releaseDragged();
this.#mouseDownHandled = false;
return;
}
if (this.#mouseDownHandled) {
return;
}
this.#mouseDownHandled = true;
const downPos = mouse.downPosition ?? mousePos ?? this.#lastMousePosition, closest = this.#findParticleUnderCursor(interactivityData, downPos);
if (!closest || !mousePos) {
if (!closest) {
this.#mouseDownHandled = false;
}
return;
}
const renderedPos = closest.getPosition();
this.#draggedParticle = closest;
this.#dragStartClickTime = mouse.clickTime;
this.#grabOffset = {
x: renderedPos.x - mousePos.x,
y: renderedPos.y - mousePos.y,
};
this.#savedVelocity = { x: closest.velocity.x, y: closest.velocity.y };
this.#dragStartClickingFalseTime = undefined;
this.#momentumSamples = [{ x: mousePos.x, y: mousePos.y, t: performance.now() }];
closest.velocity.x = zeroValue;
closest.velocity.y = zeroValue;
closest.position.x = renderedPos.x - closest.offset.x;
closest.position.y = renderedPos.y - closest.offset.y;
closest.initialPosition.x = closest.position.x;
closest.initialPosition.y = closest.position.y;
closest.misplaced = false;
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, events = (particle?.interactivity ?? container.actualOptions.interactivity)?.events;
if (this.#draggedParticle || this.#mouseDownHandled) {
return true;
}
return !!events?.onClick.enable && mouse.clicking && !!mouse.position && isInArray(dragMode, events.onClick.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "drag", Drag, ...sources);
}
reset(_interactivityData, _particle) {
}
#findParticleUnderCursor(interactivityData, mousePos) {
if (!mousePos) {
return;
}
const candidates = this.container.particles.filter(p => this.isEnabled(interactivityData, p));
let closest, closestDist = Infinity;
for (const candidate of candidates) {
const dist = getDistance(candidate.getPosition(), mousePos), radius = candidate.getRadius();
if (!Number.isFinite(dist) || !Number.isFinite(radius) || dist > radius || dist >= closestDist) {
continue;
}
closest = candidate;
closestDist = dist;
}
return closest;
}
#hasClickEnded(mouse) {
return (this.#dragStartClickTime !== undefined &&
mouse.clickTime !== undefined &&
mouse.clickTime !== this.#dragStartClickTime);
}
#releaseDragged() {
if (this.#draggedParticle) {
const dragOptions = this.container.actualOptions.interactivity?.modes.drag, preserveMomentum = dragOptions?.preserveMomentum ?? false;
if (preserveMomentum && this.#momentumSamples.length >= minSamplesForMomentum) {
const first = this.#momentumSamples[firstSampleIndex], last = this.#momentumSamples[this.#momentumSamples.length - lastSampleOffset];
if (first && last) {
const dt = last.t - first.t;
if (dt > zeroValue) {
const factor = dragOptions?.momentumFactor ?? defaultMomentumFactor;
this.#draggedParticle.velocity.x = ((last.x - first.x) / dt) * momentumToVelocityFactor * factor;
this.#draggedParticle.velocity.y = ((last.y - first.y) / dt) * momentumToVelocityFactor * factor;
}
else if (this.#savedVelocity) {
this.#draggedParticle.velocity.x = this.#savedVelocity.x;
this.#draggedParticle.velocity.y = this.#savedVelocity.y;
}
}
else if (this.#savedVelocity) {
this.#draggedParticle.velocity.x = this.#savedVelocity.x;
this.#draggedParticle.velocity.y = this.#savedVelocity.y;
}
}
else if (this.#savedVelocity) {
this.#draggedParticle.velocity.x = this.#savedVelocity.x;
this.#draggedParticle.velocity.y = this.#savedVelocity.y;
}
}
this.#draggedParticle = undefined;
this.#dragStartClickTime = undefined;
this.#dragStartClickingFalseTime = undefined;
this.#grabOffset = undefined;
this.#lastMousePosition = undefined;
this.#momentumSamples = [];
this.#savedVelocity = undefined;
}
}
async function loadExternalDragInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalDrag", container => {
return Promise.resolve(new Dragger(container));
});
});
}
class TrailColorWeight {
x = 0;
y = 0;
load(data) {
if (!data) {
return;
}
loadProperty(this, "x", data.x);
loadProperty(this, "y", data.y);
}
}
class TrailColorComponent {
value;
weights = new TrailColorWeight();
load(data) {
if (!data) {
return;
}
if (data.weights) {
this.weights ??= new TrailColorWeight();
this.weights.load(data.weights);
}
if (!isNull(data.value)) {
this.value = setRangeValue(data.value);
}
}
}
class TrailColorCoords {
h = new TrailColorComponent();
l = new TrailColorComponent();
s = new TrailColorComponent();
load(data) {
if (!data) {
return;
}
if (data.h) {
this.h ??= new TrailColorComponent();
this.h.load(data.h);
}
if (data.s) {
this.s ??= new TrailColorComponent();
this.s.load(data.s);
}
if (data.l) {
this.l ??= new TrailColorComponent();
this.l.load(data.l);
}
}
}
let Trail$1 = class Trail {
colorCoords = new TrailColorCoords();
delay = 1;
particles;
pauseOnStop = false;
quantity = 1;
load(data) {
if (isNull(data))
return;
loadProperty(this, "delay", data.delay);
loadProperty(this, "quantity", data.quantity);
loadProperty(this, "pauseOnStop", data.pauseOnStop);
if (data.particles !== undefined) {
this.particles = deepExtend({}, data.particles);
}
if (data.colorCoords) {
this.colorCoords ??= new TrailColorCoords();
this.colorCoords.load(data.colorCoords);
}
}
};
const trailMode = "trail", defaultMin = 0, defaultWeight = 0;
class TrailMaker extends ExternalInteractorBase {
maxDistance = 0;
#delay;
#lastPosition;
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#delay = 0;
}
clear() {
}
init() {
}
interact(interactivityData, delta) {
const container = this.container;
if (!container.retina.reduceFactor)
return;
const options = container.actualOptions, trailOptions = options.interactivity?.modes.trail;
if (!trailOptions)
return;
const optDelay = (trailOptions.delay * millisecondsToSeconds$1) / this.container.retina.reduceFactor;
if (this.#delay < optDelay) {
this.#delay += delta.value;
}
if (this.#delay < optDelay)
return;
const mousePos = interactivityData.mouse.position, canEmit = !(trailOptions.pauseOnStop &&
(mousePos === this.#lastPosition ||
(mousePos?.x === this.#lastPosition?.x && mousePos?.y === this.#lastPosition?.y)));
if (mousePos) {
this.#lastPosition = { ...mousePos };
}
else {
this.#lastPosition = undefined;
}
if (canEmit && mousePos) {
let particleOptions = trailOptions.particles;
const colorCoords = trailOptions.colorCoords;
if (colorCoords) {
const { width, height } = container.canvas.size, norm = {
x: mousePos.x / width,
y: mousePos.y / height,
}, hasWeights = (comp) => {
return !!(comp?.weights?.x ?? comp?.weights?.y);
}, calculateValue = (comp, originalValue, defaultMax) => {
if (!hasWeights(comp))
return undefined;
const w = comp?.weights, factor = norm.x * (w?.x ?? defaultWeight) + norm.y * (w?.y ?? defaultWeight), rangeSource = comp?.value ?? originalValue, min = getRangeMin(rangeSource ?? defaultMin), max = getRangeMax(rangeSource ?? defaultMax), result = min + factor * (max - min);
return Math.min(max, Math.max(min, result));
}, trailPaintOptions = trailOptions.particles?.paint
? itemFromSingleOrMultiple(trailOptions.particles.paint)
: undefined, fillData = trailPaintOptions?.fill, baseHsl = fillData
? rangeColorToHsl(this.#pluginManager, AnimatableColor.create(undefined, fillData.color))
: undefined, h = calculateValue(colorCoords.h, baseHsl?.h, hMax), s = calculateValue(colorCoords.s, baseHsl?.s, sMax), l = calculateValue(colorCoords.l, baseHsl?.l, lMax);
if (h !== undefined || s !== undefined || l !== undefined) {
particleOptions = deepExtend({}, trailOptions.particles, {
paint: {
fill: {
color: {
value: {
h: h ?? baseHsl?.h,
s: s ?? baseHsl?.s,
l: l ?? baseHsl?.l,
},
},
},
},
});
}
}
container.particles.push(trailOptions.quantity, mousePos, particleOptions);
}
this.#delay -= optDelay;
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
return (!!events &&
((mouse.clicking && mouse.inside && !!mouse.position && isInArray(trailMode, events.onClick.mode)) ||
(mouse.inside && !!mouse.position && isInArray(trailMode, events.onHover.mode))));
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "trail", Trail$1, ...sources);
}
reset() {
}
}
async function loadExternalTrailInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalTrail", container => {
return Promise.resolve(new TrailMaker(e.pluginManager, container));
});
});
}
var RollMode;
(function (RollMode) {
RollMode["both"] = "both";
RollMode["horizontal"] = "horizontal";
RollMode["vertical"] = "vertical";
})(RollMode || (RollMode = {}));
const maxAngle$4 = 360;
function initParticle(pluginManager, particle) {
const rollOpt = particle.options.roll;
if (!rollOpt?.enable) {
particle.roll = {
enable: false,
horizontal: false,
vertical: false,
angle: 0,
speed: 0,
};
return;
}
particle.roll = {
enable: rollOpt.enable,
horizontal: rollOpt.mode === RollMode.horizontal || rollOpt.mode === RollMode.both,
vertical: rollOpt.mode === RollMode.vertical || rollOpt.mode === RollMode.both,
angle: getRandom() * doublePI,
speed: getRangeValue(rollOpt.speed) / maxAngle$4,
};
if (rollOpt.backColor) {
particle.backColor = rangeColorToHsl(pluginManager, rollOpt.backColor);
}
else if (rollOpt.darken.enable && rollOpt.enlighten.enable) {
const alterType = getRandom() >= half$2 ? exports.AlterType.darken : exports.AlterType.enlighten;
particle.roll.alter = {
type: alterType,
value: getRangeValue(alterType === exports.AlterType.darken ? rollOpt.darken.value : rollOpt.enlighten.value),
};
}
else if (rollOpt.darken.enable) {
particle.roll.alter = {
type: exports.AlterType.darken,
value: getRangeValue(rollOpt.darken.value),
};
}
else if (rollOpt.enlighten.enable) {
particle.roll.alter = {
type: exports.AlterType.enlighten,
value: getRangeValue(rollOpt.enlighten.value),
};
}
}
function updateRoll(particle, delta) {
const roll = particle.options.roll, data = particle.roll;
if (!data || !roll?.enable) {
return;
}
const speed = data.speed * delta.factor, max = doublePI;
data.angle += speed;
if (data.angle > max) {
data.angle -= max;
}
}
class RollLight {
enable = false;
value = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "value", data.value);
}
}
class Roll {
backColor;
darken = new RollLight();
enable = false;
enlighten = new RollLight();
mode = RollMode.vertical;
speed = 25;
load(data) {
if (isNull(data)) {
return;
}
if (data.backColor !== undefined) {
this.backColor = OptionsColor.create(this.backColor, data.backColor);
}
this.darken.load(data.darken);
loadProperty(this, "enable", data.enable);
this.enlighten.load(data.enlighten);
loadProperty(this, "mode", data.mode);
loadRangeProperty(this, "speed", data.speed);
}
}
class RollUpdater {
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
getTransformValues(particle) {
const roll = particle.roll?.enable && particle.roll, rollHorizontal = roll && roll.horizontal, rollVertical = roll && roll.vertical;
return {
a: rollHorizontal ? Math.cos(roll.angle) : undefined,
d: rollVertical ? Math.sin(roll.angle) : undefined,
};
}
init(particle) {
initParticle(this.#pluginManager, particle);
}
isEnabled(particle) {
const roll = particle.options.roll;
return !particle.destroyed && !particle.spawning && !!roll?.enable;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "roll", Roll, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
updateRoll(particle, delta);
}
}
async function loadRollUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("roll", () => {
return Promise.resolve(new RollUpdater(e.pluginManager));
});
});
}
class Blend {
enable = false;
mode = "destination-out";
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "mode", data.mode);
loadProperty(this, "enable", data.enable);
}
}
class BlendPlugin {
id = "blend";
async getPlugin(container) {
const { BlendPluginInstance } = await Promise.resolve().then(function () { return BlendPluginInstance$1; });
return new BlendPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let blendOptions = options.blend;
if (!blendOptions?.load) {
options.blend = blendOptions = new Blend();
}
blendOptions.load(source?.blend);
}
loadParticlesOptions(_container, options, source) {
options.blend ??= new Blend();
options.blend.load(source?.blend);
}
needsPlugin(options) {
return !!options?.blend?.enable || !!options?.particles?.blend?.enable;
}
}
async function loadBlendPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new BlendPlugin());
});
}
const minAngle$2 = 0;
function drawCircle(data) {
const { context, particle, radius } = data;
particle.circleRange ??= { min: minAngle$2, max: doublePI };
const circleRange = particle.circleRange;
context.arc(originPoint.x, originPoint.y, radius, circleRange.min, circleRange.max, false);
}
const sides$4 = 12, maxAngle$3 = 360, minAngle$1 = 0;
class CircleDrawer {
draw(data) {
drawCircle(data);
}
getSidesCount() {
return sides$4;
}
particleInit(_container, particle) {
const shapeData = particle.shapeData, angle = shapeData?.angle ?? {
max: maxAngle$3,
min: minAngle$1,
};
particle.circleRange = isObject(angle)
? { min: degToRad(angle.min), max: degToRad(angle.max) }
: {
min: minAngle$1,
max: degToRad(angle),
};
}
}
async function loadCircleShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["circle"], () => {
return Promise.resolve(new CircleDrawer());
});
});
}
var RgbIndexes$1;
(function (RgbIndexes) {
RgbIndexes[RgbIndexes["r"] = 1] = "r";
RgbIndexes[RgbIndexes["g"] = 2] = "g";
RgbIndexes[RgbIndexes["b"] = 3] = "b";
RgbIndexes[RgbIndexes["a"] = 4] = "a";
})(RgbIndexes$1 || (RgbIndexes$1 = {}));
const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
class HexColorManager {
accepts(input) {
return input.startsWith("#");
}
handleColor(color) {
return this.#parseString(color.value);
}
handleRangeColor(color) {
return this.#parseString(color.value);
}
parseString(input) {
return this.#parseString(input);
}
#parseString(hexColor) {
if (typeof hexColor !== "string" || !this.accepts(hexColor)) {
return;
}
const hexFixed = hexColor.replace(shorthandHexRegex, (_, r, g, b, a) => {
return r + r + g + g + b + b + (a === undefined ? "" : a + a);
}), result = hexRegex.exec(hexFixed);
return result
? {
a: result[RgbIndexes$1.a] ? Number.parseInt(result[RgbIndexes$1.a], hexRadix) / alphaFactor : defaultAlpha,
b: Number.parseInt(result[RgbIndexes$1.b] ?? "0", hexRadix),
g: Number.parseInt(result[RgbIndexes$1.g] ?? "0", hexRadix),
r: Number.parseInt(result[RgbIndexes$1.r] ?? "0", hexRadix),
}
: undefined;
}
}
async function loadHexColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hex", new HexColorManager());
});
}
var HslIndexes;
(function (HslIndexes) {
HslIndexes[HslIndexes["h"] = 1] = "h";
HslIndexes[HslIndexes["s"] = 2] = "s";
HslIndexes[HslIndexes["l"] = 3] = "l";
HslIndexes[HslIndexes["a"] = 5] = "a";
})(HslIndexes || (HslIndexes = {}));
const hslRegex = /hsla?\(\s*(\d+)\s*[\s,]\s*(\d+)%\s*[\s,]\s*(\d+)%\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i;
class HslColorManager {
accepts(input) {
return input.startsWith("hsl");
}
handleColor(color) {
const colorValue = color.value, hslColor = colorValue.hsl ?? color.value;
if (!("h" in hslColor) || !("s" in hslColor) || !("l" in hslColor)) {
return;
}
return hslToRgb(hslColor);
}
handleRangeColor(color) {
const colorValue = color.value, hslColor = colorValue.hsl ?? color.value;
if (!("h" in hslColor) || !("s" in hslColor) || !("l" in hslColor)) {
return;
}
return hslToRgb({
h: getRangeValue(hslColor.h),
l: getRangeValue(hslColor.l),
s: getRangeValue(hslColor.s),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
return result
? hslaToRgba({
a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
h: Number.parseInt(result[HslIndexes.h] ?? "0", radix),
l: Number.parseInt(result[HslIndexes.l] ?? "0", radix),
s: Number.parseInt(result[HslIndexes.s] ?? "0", radix),
})
: undefined;
}
}
async function loadHslColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hsl", new HslColorManager());
});
}
function checkDestroy(particle, destroyType, value, minValue, maxValue) {
switch (destroyType) {
case exports.DestroyType.max:
if (value >= maxValue) {
particle.destroy();
}
break;
case exports.DestroyType.min:
if (value <= minValue) {
particle.destroy();
}
break;
}
}
function initParticleNumericAnimationValue(options, pxRatio) {
const valueRange = options.value, animationOptions = options.animation, res = {
delayTime: getRangeValue(animationOptions.delay) * millisecondsToSeconds$1,
enable: animationOptions.enable,
value: getRangeValue(options.value) * pxRatio,
max: getRangeMax(valueRange) * pxRatio,
min: getRangeMin(valueRange) * pxRatio,
loops: 0,
maxLoops: getRangeValue(animationOptions.count),
time: 0,
}, decayOffset = 1;
if (animationOptions.enable) {
res.decay = decayOffset - getRangeValue(animationOptions.decay);
switch (animationOptions.mode) {
case exports.AnimationMode.increase:
res.status = exports.AnimationStatus.increasing;
break;
case exports.AnimationMode.decrease:
res.status = exports.AnimationStatus.decreasing;
break;
case exports.AnimationMode.random:
res.status = getRandom() >= half$2 ? exports.AnimationStatus.increasing : exports.AnimationStatus.decreasing;
break;
}
const autoStatus = animationOptions.mode === exports.AnimationMode.auto;
switch (animationOptions.startValue) {
case exports.StartValueType.min:
res.value = res.min;
if (autoStatus) {
res.status = exports.AnimationStatus.increasing;
}
break;
case exports.StartValueType.max:
res.value = res.max;
if (autoStatus) {
res.status = exports.AnimationStatus.decreasing;
}
break;
case exports.StartValueType.random:
default:
res.value = randomInRangeValue(res);
if (autoStatus) {
res.status = getRandom() >= half$2 ? exports.AnimationStatus.increasing : exports.AnimationStatus.decreasing;
}
break;
}
}
res.initialValue = res.value;
return res;
}
function updateAnimation(particle, data, changeDirection, destroyType, delta) {
const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minDecay = 1;
if (particle.destroyed ||
!data.enable ||
((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
return;
}
const velocity = (data.velocity ?? minVelocity) * delta.factor, minValue = data.min, maxValue = data.max, decay = data.decay ?? minDecay;
data.time ??= 0;
const delayTime = data.delayTime ?? minDelay;
if (delayTime > minDelay && data.time < delayTime) {
data.time += delta.value;
if (data.time < delayTime) {
return;
}
}
switch (data.status) {
case exports.AnimationStatus.increasing:
data.value += velocity;
break;
case exports.AnimationStatus.decreasing:
data.value -= velocity;
break;
}
if (data.velocity && decay !== identity) {
data.velocity *= decay;
}
switch (data.status) {
case exports.AnimationStatus.increasing:
if (data.value >= maxValue) {
if (changeDirection) {
data.status = exports.AnimationStatus.decreasing;
}
else {
data.value -= maxValue;
}
data.loops ??= minLoops;
data.loops++;
}
break;
case exports.AnimationStatus.decreasing:
if (data.value <= minValue) {
if (changeDirection) {
data.status = exports.AnimationStatus.increasing;
}
else {
data.value += maxValue;
}
data.loops ??= minLoops;
data.loops++;
}
break;
}
checkDestroy(particle, destroyType, data.value, minValue, maxValue);
if (!particle.destroyed) {
data.value = clamp(data.value, minValue, maxValue);
}
}
class OpacityAnimation extends RangedAnimationOptions {
destroy = exports.DestroyType.none;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "destroy", data.destroy);
}
}
class Opacity extends RangedAnimationValueWithRandom {
animation = new OpacityAnimation();
value = 1;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
const animation = data.animation;
if (animation !== undefined) {
this.animation.load(animation);
}
}
}
class OpacityUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const opacityOptions = particle.options.opacity, pxRatio = 1;
if (!opacityOptions) {
return;
}
particle.opacity = initParticleNumericAnimationValue(opacityOptions, pxRatio);
const opacityAnimation = opacityOptions.animation;
if (opacityAnimation.enable) {
particle.opacity.velocity =
(getRangeValue(opacityAnimation.speed) / percentDenominator) * this.#container.retina.reduceFactor;
if (!opacityAnimation.sync) {
particle.opacity.velocity *= getRandom();
}
}
}
isEnabled(particle) {
const none = 0;
return (!particle.destroyed &&
!particle.spawning &&
!!particle.opacity &&
particle.opacity.enable &&
((particle.opacity.maxLoops ?? none) <= none ||
((particle.opacity.maxLoops ?? none) > none &&
(particle.opacity.loops ?? none) < (particle.opacity.maxLoops ?? none))));
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "opacity", Opacity, ...sources);
}
reset(particle) {
if (!particle.opacity) {
return;
}
particle.opacity.time = 0;
particle.opacity.loops = 0;
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.opacity || !particle.options.opacity) {
return;
}
updateAnimation(particle, particle.opacity, true, particle.options.opacity.animation.destroy, delta);
}
}
async function loadOpacityUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("opacity", container => {
return Promise.resolve(new OpacityUpdater(container));
});
});
}
const boundsMin = 0;
function bounceHorizontal(data) {
if ((data.outMode !== exports.OutMode.bounce && data.outMode !== exports.OutMode.split) ||
(data.direction !== exports.OutModeDirection.left && data.direction !== exports.OutModeDirection.right)) {
return;
}
if (data.bounds.right < boundsMin && data.direction === exports.OutModeDirection.left) {
data.particle.position.x = data.size + data.offset.x;
}
else if (data.bounds.left > data.canvasSize.width && data.direction === exports.OutModeDirection.right) {
data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
}
const velocity = data.particle.velocity.x;
let bounced = false;
if (data.outOfCanvas &&
((data.direction === exports.OutModeDirection.right && velocity > minVelocity) ||
(data.direction === exports.OutModeDirection.left && velocity < minVelocity))) {
const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
data.particle.velocity.x *= -newVelocity;
bounced = true;
}
if (!bounced) {
return;
}
const minPos = data.offset.x + data.size;
if (data.outOfCanvas && data.direction === exports.OutModeDirection.right) {
data.particle.position.x = data.canvasSize.width - minPos;
}
else if (data.outOfCanvas && data.direction === exports.OutModeDirection.left) {
data.particle.position.x = minPos;
}
if (data.outMode === exports.OutMode.split) {
data.particle.destroy();
}
}
function bounceVertical(data) {
if ((data.outMode !== exports.OutMode.bounce && data.outMode !== exports.OutMode.split) ||
(data.direction !== exports.OutModeDirection.bottom && data.direction !== exports.OutModeDirection.top)) {
return;
}
if (data.bounds.bottom < boundsMin && data.direction === exports.OutModeDirection.top) {
data.particle.position.y = data.size + data.offset.y;
}
else if (data.bounds.top > data.canvasSize.height && data.direction === exports.OutModeDirection.bottom) {
data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
}
const velocity = data.particle.velocity.y;
let bounced = false;
if (data.outOfCanvas &&
((data.direction === exports.OutModeDirection.bottom && velocity > minVelocity) ||
(data.direction === exports.OutModeDirection.top && velocity < minVelocity))) {
const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
data.particle.velocity.y *= -newVelocity;
bounced = true;
}
if (!bounced) {
return;
}
const minPos = data.offset.y + data.size;
if (data.outOfCanvas && data.direction === exports.OutModeDirection.bottom) {
data.particle.position.y = data.canvasSize.height - minPos;
}
else if (data.outOfCanvas && data.direction === exports.OutModeDirection.top) {
data.particle.position.y = minPos;
}
if (data.outMode === exports.OutMode.split) {
data.particle.destroy();
}
}
class BounceOutMode {
modes;
#container;
#particleBouncePlugins;
constructor(container) {
this.#container = container;
this.modes = [
exports.OutMode.bounce,
exports.OutMode.split,
];
this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
}
update(particle, direction, delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
const container = this.#container;
let handled = false;
for (const plugin of this.#particleBouncePlugins) {
handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
if (handled) {
break;
}
}
if (handled) {
return;
}
const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
}
}
class DestroyOutMode {
modes;
constructor(_container) {
this.modes = [exports.OutMode.destroy];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
switch (particle.outType) {
case exports.ParticleOutType.normal:
case exports.ParticleOutType.outside:
if (particle.isInsideCanvasForOutMode(outMode, direction)) {
return;
}
break;
case exports.ParticleOutType.inside: {
const { dx, dy } = getDistances(particle.position, particle.moveCenter), { x: vx, y: vy } = particle.velocity;
if ((vx < minVelocity && dx > particle.moveCenter.radius) ||
(vy < minVelocity && dy > particle.moveCenter.radius) ||
(vx >= minVelocity && dx < -particle.moveCenter.radius) ||
(vy >= minVelocity && dy < -particle.moveCenter.radius)) {
return;
}
break;
}
}
particle.destroy(true);
}
}
class NoneOutMode {
modes;
#container;
constructor(container) {
this.#container = container;
this.modes = [exports.OutMode.none];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
if ((particle.options.move.distance.horizontal &&
(direction === exports.OutModeDirection.left || direction === exports.OutModeDirection.right)) ??
(particle.options.move.distance.vertical &&
(direction === exports.OutModeDirection.top || direction === exports.OutModeDirection.bottom))) {
return;
}
const gravityOptions = particle.options.move.gravity, container = this.#container, canvasSize = container.canvas.size, pRadius = particle.getRadius();
if (!gravityOptions.enable) {
if ((particle.velocity.y > minVelocity && particle.position.y <= canvasSize.height + pRadius) ||
(particle.velocity.y < minVelocity && particle.position.y >= -pRadius) ||
(particle.velocity.x > minVelocity && particle.position.x <= canvasSize.width + pRadius) ||
(particle.velocity.x < minVelocity && particle.position.x >= -pRadius)) {
return;
}
if (!isPointInside(particle.position, container.canvas.size, originPoint, pRadius, direction)) {
particle.destroy();
}
}
else {
const position = particle.position;
if ((!gravityOptions.inverse &&
position.y > canvasSize.height + pRadius &&
direction === exports.OutModeDirection.bottom) ||
(gravityOptions.inverse && position.y < -pRadius && direction === exports.OutModeDirection.top)) {
particle.destroy();
}
}
}
}
const updateVector$1 = Vector.origin;
class OutOutMode {
modes;
#container;
constructor(container) {
this.#container = container;
this.modes = [exports.OutMode.out];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
const container = this.#container;
switch (particle.outType) {
case exports.ParticleOutType.inside: {
const { x: vx, y: vy } = particle.velocity;
updateVector$1.setTo(originPoint);
updateVector$1.length = particle.moveCenter.radius;
updateVector$1.angle = particle.velocity.angle + Math.PI;
updateVector$1.addTo(particle.moveCenter);
const { dx, dy } = getDistances(particle.position, updateVector$1);
if ((vx <= minVelocity && dx >= minDistance$7) ||
(vy <= minVelocity && dy >= minDistance$7) ||
(vx >= minVelocity && dx <= minDistance$7) ||
(vy >= minVelocity && dy <= minDistance$7)) {
return;
}
particle.position.x = Math.floor(randomInRangeValue({
min: 0,
max: container.canvas.size.width,
}));
particle.position.y = Math.floor(randomInRangeValue({
min: 0,
max: container.canvas.size.height,
}));
const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
particle.direction = Math.atan2(-newDy, -newDx);
particle.velocity.angle = particle.direction;
particle.justWarped = true;
break;
}
default: {
if (particle.isInsideCanvasForOutMode(outMode, direction)) {
return;
}
switch (particle.outType) {
case exports.ParticleOutType.outside: {
particle.position.x =
Math.floor(randomInRangeValue({
min: -particle.moveCenter.radius,
max: particle.moveCenter.radius,
})) + particle.moveCenter.x;
particle.position.y =
Math.floor(randomInRangeValue({
min: -particle.moveCenter.radius,
max: particle.moveCenter.radius,
})) + particle.moveCenter.y;
const { dx, dy } = getDistances(particle.position, particle.moveCenter);
if (particle.moveCenter.radius) {
particle.direction = Math.atan2(dy, dx);
particle.velocity.angle = particle.direction;
}
particle.justWarped = true;
break;
}
case exports.ParticleOutType.normal: {
const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
left: -particle.getRadius() - particle.offset.x,
right: canvasSize.width + particle.getRadius() + particle.offset.x,
top: -particle.getRadius() - particle.offset.y,
}, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
if (direction === exports.OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
particle.position.x = newPos.left;
particle.initialPosition.x = particle.position.x;
if (!warp) {
particle.position.y = getRandom() * canvasSize.height;
particle.initialPosition.y = particle.position.y;
}
particle.justWarped = true;
}
else if (direction === exports.OutModeDirection.left && nextBounds.right < -particle.offset.x) {
particle.position.x = newPos.right;
particle.initialPosition.x = particle.position.x;
if (!warp) {
particle.position.y = getRandom() * canvasSize.height;
particle.initialPosition.y = particle.position.y;
}
particle.justWarped = true;
}
if (direction === exports.OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
if (!warp) {
particle.position.x = getRandom() * canvasSize.width;
particle.initialPosition.x = particle.position.x;
}
particle.position.y = newPos.top;
particle.initialPosition.y = particle.position.y;
particle.justWarped = true;
}
else if (direction === exports.OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
if (!warp) {
particle.position.x = getRandom() * canvasSize.width;
particle.initialPosition.x = particle.position.x;
}
particle.position.y = newPos.bottom;
particle.initialPosition.y = particle.position.y;
particle.justWarped = true;
}
break;
}
}
break;
}
}
}
}
const checkOutMode = (outModes, outMode) => {
return (outModes.default === outMode ||
outModes.bottom === outMode ||
outModes.left === outMode ||
outModes.right === outMode ||
outModes.top === outMode);
};
class OutOfCanvasUpdater {
updaters;
#container;
constructor(container) {
this.#container = container;
this.updaters = new Map();
}
init(particle) {
this.#addUpdaterIfMissing(particle, exports.OutMode.bounce, container => new BounceOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.out, container => new OutOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.destroy, container => new DestroyOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.none, container => new NoneOutMode(container));
}
isEnabled(particle) {
return !particle.destroyed && !particle.spawning;
}
update(particle, delta) {
const outModes = particle.options.move.outModes;
particle.justWarped = false;
this.#updateOutMode(particle, delta, outModes.bottom ?? outModes.default, exports.OutModeDirection.bottom);
this.#updateOutMode(particle, delta, outModes.left ?? outModes.default, exports.OutModeDirection.left);
this.#updateOutMode(particle, delta, outModes.right ?? outModes.default, exports.OutModeDirection.right);
this.#updateOutMode(particle, delta, outModes.top ?? outModes.default, exports.OutModeDirection.top);
}
#addUpdaterIfMissing(particle, outMode, getUpdater) {
const outModes = particle.options.move.outModes;
if (!this.updaters.has(outMode) && checkOutMode(outModes, outMode)) {
this.updaters.set(outMode, getUpdater(this.#container));
}
}
#updateOutMode(particle, delta, outMode, direction) {
for (const updater of this.updaters.values()) {
updater.update(particle, direction, delta, outMode);
}
}
}
async function loadOutModesUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("outModes", container => {
return Promise.resolve(new OutOfCanvasUpdater(container));
});
});
}
const defaultOpacity$2 = 1;
class PaintUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
}
init(particle) {
const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates), color = paint?.color, paintColor = color ?? undefined, fill = paint?.fill, stroke = paint?.stroke;
if (fill) {
const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
particle.fillEnabled = fill.enable;
particle.fillOpacity = getRangeValue(fill.opacity);
particle.fillAnimation = fillColor.animation;
const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
if (fillHslColor) {
particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
}
}
else {
particle.fillEnabled = false;
particle.fillAnimation = undefined;
particle.fillColor = undefined;
particle.fillOpacity = defaultOpacity$2;
}
if (stroke) {
const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity$2);
particle.strokeAnimation = strokeColor.animation;
const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
if (strokeHslColor) {
particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
}
}
else {
particle.strokeAnimation = undefined;
particle.strokeColor = undefined;
particle.strokeOpacity = defaultOpacity$2;
particle.strokeWidth = 0;
}
}
isEnabled(particle) {
const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
((fillColor?.h.value !== undefined && fillColor.h.enable) ||
(fillColor?.s.value !== undefined && fillColor.s.enable) ||
(fillColor?.l.value !== undefined && fillColor.l.enable)), strokeEnabled = !!strokeAnimation &&
((strokeColor?.h.value !== undefined && strokeColor.h.enable) ||
(strokeColor?.s.value !== undefined && strokeColor.s.enable) ||
(strokeColor?.l.value !== undefined && strokeColor.l.enable));
return !particle.destroyed && !particle.spawning && (fillEnabled || strokeEnabled);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
updateColor(particle.fillColor, delta, this.#container.hdr);
updateColor(particle.strokeColor, delta, this.#container.hdr);
}
}
async function loadPaintUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("paint", container => {
return Promise.resolve(new PaintUpdater(e.pluginManager, container));
});
});
}
var RgbIndexes;
(function (RgbIndexes) {
RgbIndexes[RgbIndexes["r"] = 1] = "r";
RgbIndexes[RgbIndexes["g"] = 2] = "g";
RgbIndexes[RgbIndexes["b"] = 3] = "b";
RgbIndexes[RgbIndexes["a"] = 5] = "a";
})(RgbIndexes || (RgbIndexes = {}));
const rgbRegex = /rgba?\(\s*(\d{1,3})\s*[\s,]\s*(\d{1,3})\s*[\s,]\s*(\d{1,3})\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i;
class RgbColorManager {
accepts(input) {
return input.startsWith("rgb");
}
handleColor(color) {
const colorValue = color.value, rgbColor = colorValue.rgb ?? color.value;
if (!("r" in rgbColor) || !("g" in rgbColor) || !("b" in rgbColor)) {
return;
}
return rgbColor;
}
handleRangeColor(color) {
const colorValue = color.value, rgbColor = colorValue.rgb ?? color.value;
if (!("r" in rgbColor) || !("g" in rgbColor) || !("b" in rgbColor)) {
return;
}
return {
r: getRangeValue(rgbColor.r),
g: getRangeValue(rgbColor.g),
b: getRangeValue(rgbColor.b),
};
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
return result
? {
a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
b: parseInt(result[RgbIndexes.b] ?? "0", radix),
g: parseInt(result[RgbIndexes.g] ?? "0", radix),
r: parseInt(result[RgbIndexes.r] ?? "0", radix),
}
: undefined;
}
}
async function loadRgbColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("rgb", new RgbColorManager());
});
}
class SizeAnimation extends RangedAnimationOptions {
destroy = exports.DestroyType.none;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "destroy", data.destroy);
}
}
class Size extends RangedAnimationValueWithRandom {
animation = new SizeAnimation();
value = 3;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
const animation = data.animation;
if (animation !== undefined) {
this.animation.load(animation);
}
}
}
const minLoops = 0;
class SizeUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const container = this.#container, sizeOptions = particle.options.size;
if (!sizeOptions) {
return;
}
const sizeAnimation = sizeOptions.animation;
if (!sizeAnimation.enable) {
return;
}
particle.size.velocity = (particle.retina.sizeAnimationSpeed / percentDenominator) * container.retina.reduceFactor;
if (sizeAnimation.sync) {
return;
}
particle.size.velocity *= getRandom();
}
isEnabled(particle) {
return (!particle.destroyed &&
!particle.spawning &&
particle.size.enable &&
((particle.size.maxLoops ?? minLoops) <= minLoops ||
((particle.size.maxLoops ?? minLoops) > minLoops &&
(particle.size.loops ?? minLoops) < (particle.size.maxLoops ?? minLoops))));
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "size", Size, ...sources);
}
preInit(particle) {
const pxRatio = this.#container.retina.pixelRatio, options = particle.options, sizeOptions = options.size;
if (!sizeOptions) {
return;
}
particle.size = initParticleNumericAnimationValue(sizeOptions, pxRatio);
particle.retina.sizeAnimationSpeed = getRangeValue(sizeOptions.animation.speed) * pxRatio;
}
reset(particle) {
particle.size.time = 0;
particle.size.loops = 0;
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.options.size) {
return;
}
updateAnimation(particle, particle.size, true, particle.options.size.animation.destroy, delta);
}
}
async function loadSizeUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("size", container => {
return Promise.resolve(new SizeUpdater(container));
});
});
}
async function loadBasic(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
await Promise.all([
loadBlendPlugin(e),
loadHexColorPlugin(e),
loadHslColorPlugin(e),
loadRgbColorPlugin(e),
loadMovePlugin(e),
loadCircleShape(e),
loadPaintUpdater(e),
loadOpacityUpdater(e),
loadOutModesUpdater(e),
loadSizeUpdater(e),
]);
});
}
const easingsFunctions = new Map();
easingsFunctions.set("ease-in-quad", value => value ** 2);
easingsFunctions.set("ease-out-quad", value => 1 - (1 - value) ** 2);
easingsFunctions.set("ease-in-out-quad", value => (value < 0.5 ? 2 * value ** 2 : 1 - (-2 * value + 2) ** 2 / 2));
async function loadEasingQuadPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
for (const [easing, easingFn] of easingsFunctions) {
e.pluginManager.addEasing(easing, easingFn);
}
});
}
const validTypes$1 = ["emoji"];
function drawEmoji(data, image) {
const { context, opacity } = data, previousAlpha = context.globalAlpha, diameter = image.width, radius = diameter * half$2;
context.globalAlpha = opacity;
context.drawImage(image, -radius, -radius, diameter, diameter);
context.globalAlpha = previousAlpha;
}
const defaultFont = '"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif', noPadding = 0, firstItem = 0;
class EmojiDrawer {
#emojiShapeDict = new Map();
destroy() {
for (const [key, data] of this.#emojiShapeDict) {
if (data instanceof ImageBitmap) {
data.close();
}
this.#emojiShapeDict.delete(key);
}
}
draw(data) {
const key = data.particle.emojiDataKey;
if (!key) {
return;
}
const image = this.#emojiShapeDict.get(key);
if (!image) {
return;
}
drawEmoji(data, image);
}
async init(container) {
const options = container.actualOptions, shapeData = options.particles.shape;
if (!validTypes$1.some(t => isInArray(t, shapeData.type))) {
return;
}
const promises = [loadFont(defaultFont)], shapeOptions = validTypes$1.map(t => shapeData.options[t])[firstItem];
executeOnSingleOrMultiple(shapeOptions, shape => {
if (shape.font) {
promises.push(loadFont(shape.font));
}
});
await Promise.all(promises);
}
particleDestroy(particle) {
particle.emojiDataKey = undefined;
}
particleInit(container, particle) {
const shapeData = particle.shapeData;
if (!shapeData.value) {
return;
}
const emoji = itemFromSingleOrMultiple(shapeData.value, particle.randomIndexData);
if (!emoji) {
return;
}
const emojiOptions = typeof emoji === "string"
? {
font: shapeData.font ?? defaultFont,
padding: shapeData.padding ?? noPadding,
value: emoji,
}
: {
font: defaultFont,
padding: noPadding,
...shapeData,
...emoji,
}, font = emojiOptions.font, value = emojiOptions.value, cacheKey = `${value}_${font}`;
if (this.#emojiShapeDict.has(cacheKey)) {
particle.emojiDataKey = cacheKey;
return;
}
const padding = emojiOptions.padding * double, maxSize = getRangeMax(particle.size.value), fullSize = maxSize + padding, canvasSize = fullSize * double, cacheCanvas = new OffscreenCanvas(canvasSize, canvasSize), context = cacheCanvas.getContext("2d", container.canvas.render.settings);
if (!context) {
return;
}
context.font = `400 ${(maxSize * double).toString()}px ${font}`;
context.textBaseline = "middle";
context.textAlign = "center";
context.fillText(value, fullSize, fullSize);
const image = cacheCanvas instanceof HTMLCanvasElement ? cacheCanvas : cacheCanvas.transferToImageBitmap();
this.#emojiShapeDict.set(cacheKey, image);
particle.emojiDataKey = cacheKey;
}
}
async function loadEmojiShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(validTypes$1, () => Promise.resolve(new EmojiDrawer()));
});
}
const minFactor = 1, minRadius$5 = 0, updateVector = Vector.origin;
function processAttract(pluginManager, container, position, attractRadius, area, queryCb, onAttractParticle) {
const attractOptions = container.actualOptions.interactivity?.modes.attract;
if (!attractOptions) {
return;
}
const query = container.particles.grid.query(area, queryCb);
for (const particle of query) {
const { dx, dy, distance } = getDistances(particle.position, position), velocity = attractOptions.speed * attractOptions.factor, attractFactor = clamp(pluginManager.getEasing(attractOptions.easing)(identity$3 - distance / attractRadius) * velocity, minFactor, attractOptions.maxSpeed);
updateVector.x = !distance ? velocity : (dx / distance) * attractFactor;
updateVector.y = !distance ? velocity : (dy / distance) * attractFactor;
onAttractParticle?.(particle);
particle.position.subFrom(updateVector);
}
}
function clickAttract(pluginManager, container, interactivityData, enabledCb, onAttractParticle) {
container.attract ??= { particles: [] };
const { attract } = container;
if (!attract.finish) {
attract.count ??= 0;
attract.count++;
if (attract.count === container.particles.count) {
attract.finish = true;
}
}
if (attract.clicking) {
const mousePos = interactivityData.mouse.clickPosition, attractRadius = container.retina.attractModeDistance;
if (!attractRadius || attractRadius < minRadius$5 || !mousePos) {
return;
}
processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p), onAttractParticle);
}
else if (attract.clicking === false) {
attract.particles = [];
}
}
function hoverAttract(pluginManager, container, interactivityData, enabledCb, onAttractParticle) {
const mousePos = interactivityData.mouse.position, attractRadius = container.retina.attractModeDistance;
if (!attractRadius || attractRadius < minRadius$5 || !mousePos) {
return;
}
processAttract(pluginManager, container, mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius), (p) => enabledCb(p), onAttractParticle);
}
let Attract$1 = class Attract {
distance = 200;
duration = 0.4;
easing = "ease-out-quad";
factor = 1;
maxSpeed = 50;
restore;
speed = 1;
constructor() {
this.restore = {
enable: false,
delay: 0,
speed: 0.08,
follow: true,
};
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
loadProperty(this, "duration", data.duration);
if (data.easing !== undefined) {
this.easing = data.easing;
}
loadProperty(this, "factor", data.factor);
if (data.maxSpeed !== undefined) {
this.maxSpeed = data.maxSpeed;
}
loadProperty(this, "speed", data.speed);
if (data.restore !== undefined) {
this.restore.enable = data.restore.enable ?? this.restore.enable;
this.restore.delay = data.restore.delay ?? this.restore.delay;
this.restore.speed = data.restore.speed ?? this.restore.speed;
this.restore.follow = data.restore.follow ?? this.restore.follow;
}
}
};
const attractMode = "attract", minVelocityLengthSq = 0, minRestoreSpeed$1 = 0.001, maxRestoreSpeed$1 = 1, restoreEpsilon$1 = 0.5;
let Attractor$1 = class Attractor extends ExternalInteractorBase {
handleClickMode;
#interactedThisFrame;
#maxDistance;
#pluginManager;
#restoreData;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#maxDistance = 0;
this.#interactedThisFrame = new Set();
this.#restoreData = new Map();
container.attract ??= { particles: [] };
this.handleClickMode = (mode, interactivityData) => {
const options = this.container.actualOptions, attract = options.interactivity?.modes.attract;
if (!attract || mode !== attractMode) {
return;
}
container.attract ??= { particles: [] };
container.attract.clicking = true;
container.attract.count = 0;
for (const particle of container.attract.particles) {
if (!this.isEnabled(interactivityData, particle)) {
continue;
}
particle.velocity.setTo(particle.initialVelocity);
}
container.attract.particles = [];
container.attract.finish = false;
setTimeout(() => {
if (container.destroyed) {
return;
}
container.attract ??= { particles: [] };
container.attract.clicking = false;
}, attract.duration * millisecondsToSeconds$1);
};
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, attract = container.actualOptions.interactivity?.modes.attract;
if (!attract) {
return;
}
this.#maxDistance = attract.distance;
container.retina.attractModeDistance = attract.distance * container.retina.pixelRatio;
}
interact(interactivityData) {
this.#interactedThisFrame.clear();
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
if (!events) {
return;
}
const { enable: hoverEnabled, mode: hoverMode } = events.onHover, { enable: clickEnabled, mode: clickMode } = events.onClick;
if (mouseMoveStatus && hoverEnabled && isInArray(attractMode, hoverMode)) {
hoverAttract(this.#pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p), p => {
this.#trackInteractedParticle(p);
});
}
else if (clickEnabled && isInArray(attractMode, clickMode)) {
clickAttract(this.#pluginManager, this.container, interactivityData, p => this.isEnabled(interactivityData, p), p => {
this.#trackInteractedParticle(p);
});
}
this.#restoreParticles();
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
if ((!mouse.position || !events?.onHover.enable) && (!mouse.clickPosition || !events?.onClick.enable)) {
return false;
}
const hoverMode = events.onHover.mode, clickMode = events.onClick.mode;
return isInArray(attractMode, hoverMode) || isInArray(attractMode, clickMode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "attract", Attract$1, ...sources);
}
reset() {
}
#restoreParticles() {
const restore = this.container.actualOptions.interactivity?.modes.attract?.restore;
if (!restore?.enable || !this.#restoreData.size) {
return;
}
const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds$1, restoreSpeed = Math.max(minRestoreSpeed$1, Math.min(maxRestoreSpeed$1, restore.speed));
for (const [particle, restoreData] of this.#restoreData) {
if (this.#interactedThisFrame.has(particle)) {
continue;
}
if (particle.destroyed) {
this.#restoreData.delete(particle);
continue;
}
const target = restoreData.target;
if (now - restoreData.lastInteractionTime < restoreDelay) {
continue;
}
let dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
if (restore.follow && particle.options.move.enable) {
const { x: vx, y: vy, z: vz } = particle.velocity, velocityLengthSq = vx * vx + vy * vy + vz * vz;
if (velocityLengthSq > minVelocityLengthSq) {
const parallelScale = (dx * vx + dy * vy + dz * vz) / velocityLengthSq;
dx -= vx * parallelScale;
dy -= vy * parallelScale;
dz -= vz * parallelScale;
}
}
particle.position.x += dx * restoreSpeed;
particle.position.y += dy * restoreSpeed;
particle.position.z += dz * restoreSpeed;
if (Math.abs(dx) <= restoreEpsilon$1 && Math.abs(dy) <= restoreEpsilon$1) {
particle.position.x = target.x;
particle.position.y = target.y;
particle.position.z = target.z;
this.#restoreData.delete(particle);
continue;
}
}
}
#trackInteractedParticle(particle) {
this.#interactedThisFrame.add(particle);
const restore = this.container.actualOptions.interactivity?.modes.attract?.restore;
if (!restore?.enable) {
return;
}
const now = Date.now();
let restoreData = this.#restoreData.get(particle);
if (!restoreData) {
restoreData = {
target: particle.position.copy(),
lastInteractionTime: now,
};
this.#restoreData.set(particle, restoreData);
}
restoreData.lastInteractionTime = now;
}
};
async function loadExternalAttractInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalAttract", container => {
return Promise.resolve(new Attractor$1(e.pluginManager, container));
});
});
}
const halfPI$3 = Math.PI * half$2, toleranceFactor$1 = 10;
function processBounce(container, position, radius, area, enabledCb) {
const query = container.particles.grid.query(area, enabledCb);
for (const particle of query) {
if (area instanceof Circle) {
circleBounce(circleBounceDataFromParticle(particle), {
position,
mass: radius ** squareExp * halfPI$3,
velocity: Vector.origin,
factor: Vector.origin,
});
}
else if (area instanceof Rectangle) {
rectBounce(particle, calculateBounds(position, radius));
}
}
}
function singleSelectorBounce(container, selector, div, bounceCb) {
const query = safeDocument().querySelectorAll(selector);
if (!query.length) {
return;
}
query.forEach(item => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth * half$2) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight * half$2) * pxRatio,
}, radius = elem.offsetWidth * half$2 * pxRatio, tolerance = toleranceFactor$1 * pxRatio, area = div.type === DivType.circle
? new Circle(pos.x, pos.y, radius + tolerance)
: new Rectangle(elem.offsetLeft * pxRatio - tolerance, elem.offsetTop * pxRatio - tolerance, elem.offsetWidth * pxRatio + tolerance * double, elem.offsetHeight * pxRatio + tolerance * double);
bounceCb(pos, radius, area);
});
}
function divBounce(container, divs, bounceMode, enabledCb) {
divModeExecute(bounceMode, divs, (selector, div) => {
singleSelectorBounce(container, selector, div, (pos, radius, area) => {
processBounce(container, pos, radius, area, enabledCb);
});
});
}
function mouseBounce(container, interactivityData, enabledCb) {
const pxRatio = container.retina.pixelRatio, tolerance = toleranceFactor$1 * pxRatio, mousePos = interactivityData.mouse.position, radius = container.retina.bounceModeDistance;
if (!radius || radius < minRadius$8 || !mousePos) {
return;
}
processBounce(container, mousePos, radius, new Circle(mousePos.x, mousePos.y, radius + tolerance), enabledCb);
}
function rectSideBounce(data) {
const res = { bounced: false }, { pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor } = data;
if (pOtherSide.min < rectOtherSide.min ||
pOtherSide.min > rectOtherSide.max ||
pOtherSide.max < rectOtherSide.min ||
pOtherSide.max > rectOtherSide.max) {
return res;
}
if ((pSide.max >= rectSide.min && pSide.max <= (rectSide.max + rectSide.min) * half$2 && velocity > minVelocity) ||
(pSide.min <= rectSide.max && pSide.min > (rectSide.max + rectSide.min) * half$2 && velocity < minVelocity)) {
res.velocity = velocity * -factor;
res.bounced = true;
}
return res;
}
function rectBounce(particle, divBounds) {
const pPos = particle.getPosition(), size = particle.getRadius(), bounds = calculateBounds(pPos, size), bounceOptions = particle.options.bounce, resH = rectSideBounce({
pSide: {
min: bounds.left,
max: bounds.right,
},
pOtherSide: {
min: bounds.top,
max: bounds.bottom,
},
rectSide: {
min: divBounds.left,
max: divBounds.right,
},
rectOtherSide: {
min: divBounds.top,
max: divBounds.bottom,
},
velocity: particle.velocity.x,
factor: getRangeValue(bounceOptions.horizontal.value),
});
if (resH.bounced) {
if (resH.velocity !== undefined) {
particle.velocity.x = resH.velocity;
}
if (resH.position !== undefined) {
particle.position.x = resH.position;
}
}
const resV = rectSideBounce({
pSide: {
min: bounds.top,
max: bounds.bottom,
},
pOtherSide: {
min: bounds.left,
max: bounds.right,
},
rectSide: {
min: divBounds.top,
max: divBounds.bottom,
},
rectOtherSide: {
min: divBounds.left,
max: divBounds.right,
},
velocity: particle.velocity.y,
factor: getRangeValue(bounceOptions.vertical.value),
});
if (resV.bounced) {
if (resV.velocity !== undefined) {
particle.velocity.y = resV.velocity;
}
if (resV.position !== undefined) {
particle.position.y = resV.position;
}
}
}
class Bounce {
distance = 200;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
}
}
const bounceMode = "bounce";
class Bouncer extends ExternalInteractorBase {
#maxDistance;
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, bounce = container.actualOptions.interactivity?.modes.bounce;
if (!bounce) {
return;
}
this.#maxDistance = bounce.distance;
container.retina.bounceModeDistance = bounce.distance * container.retina.pixelRatio;
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions, events = options.interactivity?.events, mouseMoveStatus = interactivityData.status === mouseMoveEvent;
if (!events) {
return;
}
const hoverEnabled = events.onHover.enable, hoverMode = events.onHover.mode, divs = events.onDiv;
if (mouseMoveStatus && hoverEnabled && isInArray(bounceMode, hoverMode)) {
mouseBounce(this.container, interactivityData, p => this.isEnabled(interactivityData, p));
}
else {
divBounce(this.container, divs, bounceMode, p => this.isEnabled(interactivityData, p));
}
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
if (!events) {
return false;
}
const divs = events.onDiv;
return ((!!mouse.position && events.onHover.enable && isInArray(bounceMode, events.onHover.mode)) ||
isDivModeEnabled(bounceMode, divs));
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "bounce", Bounce, ...sources);
}
reset() {
}
}
async function loadExternalBounceInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalBounce", container => {
return Promise.resolve(new Bouncer(container));
});
});
}
class BubbleBase {
color;
distance = 200;
duration = 0.4;
mix = false;
opacity;
size;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
loadProperty(this, "duration", data.duration);
loadProperty(this, "mix", data.mix);
loadRangeProperty(this, "opacity", data.opacity);
if (data.color !== undefined) {
const sourceColor = isArray(this.color) ? undefined : this.color;
this.color = executeOnSingleOrMultiple(data.color, color => {
return OptionsColor.create(sourceColor, color);
});
}
loadProperty(this, "size", data.size);
}
}
class BubbleDiv extends BubbleBase {
selectors = [];
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "selectors", data.selectors);
}
}
class Bubble extends BubbleBase {
divs;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
this.divs = executeOnSingleOrMultiple(data.divs, div => {
const tmp = new BubbleDiv();
tmp.load(div);
return tmp;
});
}
}
class BubbleModifier {
div;
enabled = false;
fillColor;
finalColor;
id = "bubble";
inRange = false;
opacity;
priority = 100;
radius;
strokeColor;
}
var ProcessBubbleType;
(function (ProcessBubbleType) {
ProcessBubbleType["color"] = "color";
ProcessBubbleType["opacity"] = "opacity";
ProcessBubbleType["size"] = "size";
})(ProcessBubbleType || (ProcessBubbleType = {}));
function calculateBubbleValue(particleValue, modeValue, optionsValue, ratio) {
if (modeValue >= optionsValue) {
const value = particleValue + (modeValue - optionsValue) * ratio;
return clamp(value, particleValue, modeValue);
}
else if (modeValue < optionsValue) {
const value = particleValue - (optionsValue - modeValue) * ratio;
return clamp(value, modeValue, particleValue);
}
return undefined;
}
const bubbleMode = "bubble", minDistance$5 = 0, defaultClickTime = 0, defaultOpacity$1 = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, defaultRatio = 1;
class Bubbler extends ExternalInteractorBase {
handleClickMode;
#maxDistance;
#modifiers = new WeakMap();
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#maxDistance = 0;
container.bubble ??= {};
this.handleClickMode = (mode) => {
if (mode !== bubbleMode) {
return;
}
container.bubble ??= {};
container.bubble.clicking = true;
};
}
get maxDistance() {
return this.#maxDistance;
}
clear(particle, _delta, force) {
const mod = this.#modifiers.get(particle);
if (mod?.inRange && !force) {
return;
}
particle.removeModifier(bubbleMode);
this.#modifiers.delete(particle);
}
getOrCreateModifier(particle) {
let mod = this.#modifiers.get(particle);
if (!mod) {
mod = new BubbleModifier();
this.#modifiers.set(particle, mod);
particle.addModifier(mod);
}
return mod;
}
init() {
const container = this.container, bubble = container.actualOptions.interactivity?.modes.bubble;
if (!bubble) {
return;
}
this.#maxDistance = bubble.distance;
container.retina.bubbleModeDistance = bubble.distance * container.retina.pixelRatio;
if (bubble.size !== undefined) {
container.retina.bubbleModeSize = bubble.size * container.retina.pixelRatio;
}
}
interact(interactivityData, delta) {
const options = this.container.actualOptions, events = options.interactivity?.events;
if (!events) {
return;
}
const onHover = events.onHover, onClick = events.onClick, hoverEnabled = onHover.enable, hoverMode = onHover.mode, clickEnabled = onClick.enable, clickMode = onClick.mode, divs = events.onDiv;
if (hoverEnabled && isInArray(bubbleMode, hoverMode)) {
this.#hoverBubble(interactivityData);
}
else if (clickEnabled && isInArray(bubbleMode, clickMode)) {
this.#clickBubble(interactivityData);
}
else {
divModeExecute(bubbleMode, divs, (selector, div) => {
this.#singleSelectorHover(interactivityData, delta, selector, div);
});
}
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
if (!events) {
return false;
}
const { onClick, onDiv, onHover } = events, divBubble = isDivModeEnabled(bubbleMode, onDiv);
if (!(divBubble || (onHover.enable && !!mouse.position) || (onClick.enable && mouse.clickPosition))) {
return false;
}
return isInArray(bubbleMode, onHover.mode) || isInArray(bubbleMode, onClick.mode) || divBubble;
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "bubble", Bubble, ...sources);
}
reset(_interactivityData, particle) {
const mod = this.#modifiers.get(particle);
if (mod) {
mod.enabled = false;
mod.inRange = false;
}
}
#clickBubble(interactivityData) {
const container = this.container, options = container.actualOptions, mouseClickPos = interactivityData.mouse.clickPosition, bubbleOptions = options.interactivity?.modes.bubble;
if (!bubbleOptions || !mouseClickPos) {
return;
}
container.bubble ??= {};
const distance = container.retina.bubbleModeDistance;
if (!distance || distance < minDistance$5) {
return;
}
const query = container.particles.grid.queryCircle(mouseClickPos, distance, p => this.isEnabled(interactivityData, p)), { bubble } = container;
for (const particle of query) {
if (!bubble.clicking) {
continue;
}
const mod = this.getOrCreateModifier(particle);
mod.enabled = !bubble.durationEnd;
mod.inRange = !bubble.durationEnd;
const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (performance.now() - (interactivityData.mouse.clickTime ?? defaultClickTime)) / millisecondsToSeconds$1;
if (timeSpent > bubbleOptions.duration) {
bubble.durationEnd = true;
}
if (timeSpent > bubbleOptions.duration * double) {
bubble.clicking = false;
bubble.durationEnd = false;
}
const sizeData = {
bubbleObj: {
optValue: container.retina.bubbleModeSize,
value: mod.radius,
},
particlesObj: {
optValue: particle.size.max,
value: particle.size.value,
},
type: ProcessBubbleType.size,
};
this.#process(particle, distMouse, timeSpent, sizeData);
const opacityData = {
bubbleObj: {
optValue: bubbleOptions.opacity,
value: mod.opacity,
},
particlesObj: {
optValue: particle.opacity?.max ?? defaultOpacity$1,
value: particle.opacity?.value ?? defaultOpacity$1,
},
type: ProcessBubbleType.opacity,
};
this.#process(particle, distMouse, timeSpent, opacityData);
if (!bubble.durationEnd && distMouse <= distance) {
this.#hoverBubbleColor(particle, distMouse);
}
else {
mod.fillColor = undefined;
mod.strokeColor = undefined;
}
}
}
#hoverBubble(interactivityData) {
const container = this.container, mousePos = interactivityData.mouse.position, distance = container.retina.bubbleModeDistance;
if (!distance || distance < minDistance$5 || !mousePos) {
return;
}
const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
for (const particle of query) {
const mod = this.getOrCreateModifier(particle);
mod.enabled = true;
mod.inRange = true;
const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = ratioOffset - pointDistance / distance;
if (pointDistance <= distance) {
if (ratio >= minRatio && interactivityData.status === mouseMoveEvent) {
this.#hoverBubbleSize(particle, ratio);
this.#hoverBubbleOpacity(particle, ratio);
this.#hoverBubbleColor(particle, ratio);
}
}
else {
this.reset(interactivityData, particle);
}
if (interactivityData.status === mouseLeaveEvent) {
this.reset(interactivityData, particle);
}
}
}
#hoverBubbleColor(particle, ratio, divBubble) {
const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity?.modes.bubble, mod = this.getOrCreateModifier(particle);
if (!bubbleOptions) {
return;
}
if (!mod.finalColor) {
const modeColor = bubbleOptions.color;
if (!modeColor) {
return;
}
const bubbleColor = itemFromSingleOrMultiple(modeColor);
mod.finalColor = rangeColorToHsl(this.#pluginManager, bubbleColor);
}
if (!mod.finalColor) {
return;
}
if (bubbleOptions.mix) {
mod.fillColor = undefined;
mod.strokeColor = undefined;
const pColor = particle.getFillColor();
if (pColor) {
const mixedColor = rgbToHsl(colorMix(pColor, mod.finalColor, ratioOffset - ratio, ratio, this.container.hdr));
mod.fillColor = mixedColor;
mod.strokeColor = mixedColor;
}
else {
mod.fillColor = mod.finalColor;
mod.strokeColor = mod.finalColor;
}
}
else {
mod.fillColor = mod.finalColor;
mod.strokeColor = mod.finalColor;
}
}
#hoverBubbleOpacity(particle, ratio, divBubble) {
const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity?.modes.bubble?.opacity;
if (!modeOpacity) {
return;
}
const pOpacity = particle.opacity?.value ?? defaultOpacity$1, opacity = calculateBubbleValue(pOpacity, modeOpacity, particle.opacity?.max ?? defaultOpacity$1, ratio);
if (opacity !== undefined) {
const mod = this.getOrCreateModifier(particle);
mod.opacity = opacity;
}
}
#hoverBubbleSize(particle, ratio, divBubble) {
const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
if (modeSize === undefined) {
return;
}
const pSize = particle.size.value, size = calculateBubbleValue(pSize, modeSize, particle.size.max, ratio);
if (size !== undefined) {
const mod = this.getOrCreateModifier(particle);
mod.radius = size;
}
}
#process(particle, distMouse, timeSpent, data) {
const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity?.modes.bubble;
if (!bubbleOptions || bubbleParam === undefined) {
return;
}
const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type;
if (!bubbleDistance || bubbleDistance < minDistance$5 || bubbleParam === particlesParam) {
return;
}
container.bubble ??= {};
const mod = this.getOrCreateModifier(particle);
if (container.bubble.durationEnd) {
if (pObjBubble) {
if (type === ProcessBubbleType.size) {
mod.radius = undefined;
}
if (type === ProcessBubbleType.opacity) {
mod.opacity = undefined;
}
}
}
else {
if (distMouse <= bubbleDistance) {
const obj = pObjBubble ?? pObj;
if (obj !== bubbleParam) {
const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
if (type === ProcessBubbleType.size) {
mod.radius = value;
}
if (type === ProcessBubbleType.opacity) {
mod.opacity = value;
}
}
}
else {
if (type === ProcessBubbleType.size) {
mod.radius = undefined;
}
if (type === ProcessBubbleType.opacity) {
mod.opacity = undefined;
}
}
}
}
#singleSelectorHover(interactivityData, delta, selector, div) {
const container = this.container, selectors = safeDocument().querySelectorAll(selector), bubble = container.actualOptions.interactivity?.modes.bubble;
if (!bubble || !selectors.length) {
return;
}
selectors.forEach(item => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth * half$2) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight * half$2) * pxRatio,
}, repulseRadius = elem.offsetWidth * half$2 * pxRatio, area = div.type === DivType.circle
? new Circle(pos.x, pos.y, repulseRadius)
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), query = container.particles.grid.query(area, p => this.isEnabled(interactivityData, p));
for (const particle of query) {
if (!area.contains(particle.getPosition())) {
continue;
}
const mod = this.getOrCreateModifier(particle);
mod.enabled = true;
mod.inRange = true;
const divs = bubble.divs, divBubble = divMode(divs, elem);
if (!mod.div || mod.div !== elem) {
this.clear(particle, delta, true);
mod.div = elem;
}
this.#hoverBubbleSize(particle, defaultRatio, divBubble);
this.#hoverBubbleOpacity(particle, defaultRatio, divBubble);
this.#hoverBubbleColor(particle, defaultRatio, divBubble);
}
});
}
}
async function loadExternalBubbleInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalBubble", container => {
return Promise.resolve(new Bubbler(e.pluginManager, container));
});
});
}
class ConnectLinks {
opacity = 0.5;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "opacity", data.opacity);
}
}
class Connect {
distance = 80;
links = new ConnectLinks();
radius = 60;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
this.links.load(data.links);
loadProperty(this, "radius", data.radius);
}
}
const gradientMin = 0, gradientMax = 1, defaultLinksWidth = 0;
function gradient(container, context, p1, p2, opacity) {
const gradStop = Math.floor(p2.getRadius() / p1.getRadius()), color1 = p1.getFillColor(), color2 = p2.getFillColor();
if (!color1 || !color2) {
return;
}
const sourcePos = p1.getPosition(), destPos = p2.getPosition(), midRgb = colorMix(color1, color2, p1.getRadius(), p2.getRadius(), container.hdr), grad = context.createLinearGradient(sourcePos.x, sourcePos.y, destPos.x, destPos.y);
grad.addColorStop(gradientMin, getStyleFromHsl(color1, container.hdr, opacity, container.peakNits, container.hdrMode));
grad.addColorStop(clamp(gradStop, gradientMin, gradientMax), getStyleFromRgb(midRgb, container.hdr, opacity, container.peakNits, container.hdrMode));
grad.addColorStop(gradientMax, getStyleFromHsl(color2, container.hdr, opacity, container.peakNits, container.hdrMode));
return grad;
}
function drawConnectLine(context, width, lineStyle, begin, end) {
drawLine$1(context, begin, end);
context.lineWidth = width;
context.strokeStyle = lineStyle;
context.stroke();
}
function lineStyle(container, ctx, p1, p2) {
const options = container.actualOptions, connectOptions = options.interactivity?.modes.connect;
if (!connectOptions) {
return;
}
return gradient(container, ctx, p1, p2, connectOptions.links.opacity);
}
function drawConnection(container, p1, p2) {
container.canvas.render.draw(ctx => {
const ls = lineStyle(container, ctx, p1, p2);
if (!ls) {
return;
}
const pos1 = p1.getPosition(), pos2 = p2.getPosition();
drawConnectLine(ctx, p1.retina.linksWidth ?? defaultLinksWidth, ls, pos1, pos2);
});
}
const connectMode = "connect", minDistance$4 = 0;
class Connector extends ExternalInteractorBase {
#maxDistance;
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, connect = container.actualOptions.interactivity?.modes.connect;
if (!connect) {
return;
}
this.#maxDistance = connect.distance;
container.retina.connectModeDistance = connect.distance * container.retina.pixelRatio;
container.retina.connectModeRadius = connect.radius * container.retina.pixelRatio;
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions;
if (options.interactivity?.events.onHover.enable && interactivityData.status === "pointermove") {
const mousePos = interactivityData.mouse.position, { connectModeDistance, connectModeRadius } = container.retina;
if (!connectModeDistance ||
connectModeDistance < minDistance$4 ||
!connectModeRadius ||
connectModeRadius < minDistance$4 ||
!mousePos) {
return;
}
const distance = Math.abs(connectModeRadius), query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
query.forEach((p1, i) => {
const pos1 = p1.getPosition(), indexOffset = 1;
for (const p2 of query.slice(i + indexOffset)) {
const pos2 = p2.getPosition(), distMax = Math.abs(connectModeDistance), xDiff = Math.abs(pos1.x - pos2.x), yDiff = Math.abs(pos1.y - pos2.y);
if (xDiff < distMax && yDiff < distMax) {
drawConnection(container, p1, p2);
}
}
});
}
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, events = (particle?.interactivity ?? container.actualOptions.interactivity)?.events;
if (!(events?.onHover.enable && mouse.position)) {
return false;
}
return isInArray(connectMode, events.onHover.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "connect", Connect, ...sources);
}
reset() {
}
}
async function loadExternalConnectInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalConnect", container => {
return Promise.resolve(new Connector(container));
});
});
}
const toleranceFactor = 10;
function processDestroy(container, area) {
const query = container.particles.grid.query(area);
for (const particle of query) {
particle.destroy();
}
}
function singleSelectorDestroy(container, selector, div) {
const query = safeDocument().querySelectorAll(selector);
if (!query.length) {
return;
}
query.forEach(item => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth * half$2) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight * half$2) * pxRatio,
}, radius = elem.offsetWidth * half$2 * pxRatio, tolerance = toleranceFactor * pxRatio, area = div.type === DivType.circle
? new Circle(pos.x, pos.y, radius + tolerance)
: new Rectangle(elem.offsetLeft * pxRatio - tolerance, elem.offsetTop * pxRatio - tolerance, elem.offsetWidth * pxRatio + tolerance * double, elem.offsetHeight * pxRatio + tolerance * double);
processDestroy(container, area);
});
}
function divDestroy(container, divs, destroyMode) {
divModeExecute(destroyMode, divs, (selector, div) => {
singleSelectorDestroy(container, selector, div);
});
}
function mouseDestroy(container, interactivityData) {
const pxRatio = container.retina.pixelRatio, tolerance = toleranceFactor * pxRatio, mousePos = interactivityData.mouse.position, radius = container.retina.destroyModeDistance;
if (!radius || radius < minRadius$8 || !mousePos) {
return;
}
processDestroy(container, new Circle(mousePos.x, mousePos.y, radius + tolerance));
}
class Destroy {
distance = 200;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
}
}
const destroyMode = "destroy";
class Destroyer extends ExternalInteractorBase {
#maxDistance;
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, destroy = container.actualOptions.interactivity?.modes.destroy;
if (!destroy) {
return;
}
this.#maxDistance = destroy.distance;
container.retina.destroyModeDistance = destroy.distance * container.retina.pixelRatio;
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions, events = options.interactivity?.events, mouseMoveStatus = interactivityData.status === mouseMoveEvent;
if (!events) {
return;
}
const hoverEnabled = events.onHover.enable, hoverMode = events.onHover.mode, divs = events.onDiv;
if (mouseMoveStatus && hoverEnabled && isInArray(destroyMode, hoverMode)) {
mouseDestroy(this.container, interactivityData);
}
else {
divDestroy(this.container, divs, destroyMode);
}
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
if (!events) {
return false;
}
const divs = events.onDiv;
return ((!!mouse.position && events.onHover.enable && isInArray(destroyMode, events.onHover.mode)) ||
isDivModeEnabled(destroyMode, divs));
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "destroy", Destroy, ...sources);
}
reset() {
}
}
async function loadExternalDestroyInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalDestroy", async (container) => {
return Promise.resolve(new Destroyer(container));
});
});
}
class GrabLinks {
blink = false;
color;
consent = false;
opacity = 1;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "blink", data.blink);
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "consent", data.consent);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class Grab {
distance = 100;
links = new GrabLinks();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
this.links.load(data.links);
}
}
const defaultWidth$2 = 0;
function drawGrabLine(context, width, begin, end, colorLine, opacity, hdr = false, peakNits, mode) {
drawLine$1(context, begin, end);
context.strokeStyle = getStyleFromRgb(colorLine, hdr, opacity, peakNits, mode);
context.lineWidth = width;
context.stroke();
}
function drawGrab(container, particle, lineColor, opacity, mousePos) {
container.canvas.render.draw(ctx => {
const beginPos = particle.getPosition();
drawGrabLine(ctx, particle.retina.linksWidth ?? defaultWidth$2, beginPos, mousePos, lineColor, opacity, container.hdr, container.peakNits, container.hdrMode);
});
}
const grabMode = "grab", minDistance$3 = 0, minOpacity$1 = 0;
class Grabber extends ExternalInteractorBase {
#maxDistance;
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, grab = container.actualOptions.interactivity?.modes.grab;
if (!grab) {
return;
}
this.#maxDistance = grab.distance;
container.retina.grabModeDistance = grab.distance * container.retina.pixelRatio;
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions, interactivity = options.interactivity;
if (!interactivity?.modes.grab ||
!interactivity.events.onHover.enable ||
interactivityData.status !== mouseMoveEvent) {
return;
}
const mousePos = interactivityData.mouse.position;
if (!mousePos) {
return;
}
const distance = container.retina.grabModeDistance;
if (!distance || distance < minDistance$3) {
return;
}
const query = container.particles.grid.queryCircle(mousePos, distance, p => this.isEnabled(interactivityData, p));
for (const particle of query) {
const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos);
if (pointDistance > distance) {
continue;
}
const grabLineOptions = interactivity.modes.grab.links, lineOpacity = grabLineOptions.opacity, opacityLine = lineOpacity - (pointDistance * lineOpacity) / distance;
if (opacityLine <= minOpacity$1) {
continue;
}
const optColor = grabLineOptions.color ?? particle.options.links?.color;
if (!container.particles.grabLineColor && optColor) {
const linksOptions = interactivity.modes.grab.links;
container.particles.grabLineColor = getLinkRandomColor(this.#pluginManager, optColor, linksOptions.blink, linksOptions.consent);
}
const colorLine = getLinkColor(particle, undefined, container.particles.grabLineColor, container.hdr);
if (!colorLine) {
continue;
}
drawGrab(container, particle, colorLine, opacityLine, mousePos);
}
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, events = (particle?.interactivity ?? container.actualOptions.interactivity)?.events;
return !!events?.onHover.enable && !!mouse.position && isInArray(grabMode, events.onHover.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "grab", Grab, ...sources);
}
reset() {
}
}
async function loadExternalGrabInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalGrab", container => {
return Promise.resolve(new Grabber(e.pluginManager, container));
});
});
}
class Parallax {
force = 2;
smooth = 10;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "force", data.force);
loadProperty(this, "smooth", data.smooth);
}
}
const parallaxMode = "parallax";
class Parallaxer extends ExternalInteractorBase {
maxDistance = 0;
constructor(container) {
super(container);
}
clear() {
}
init() {
}
interact(interactivityData) {
for (const particle of this.container.particles.filter(p => this.isEnabled(interactivityData, p))) {
this.#parallaxInteract(interactivityData, particle);
}
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, events = (particle?.interactivity ?? container.actualOptions.interactivity)?.events;
return !!events?.onHover.enable && !!mouse.position && isInArray(parallaxMode, events.onHover.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "parallax", Parallax, ...sources);
}
reset() {
}
#parallaxInteract(interactivityData, particle) {
if (!this.isEnabled(interactivityData, particle)) {
return;
}
const container = this.container, options = container.actualOptions, parallaxOptions = options.interactivity?.modes.parallax;
if (!parallaxOptions) {
return;
}
const parallaxForce = parallaxOptions.force, mousePos = interactivityData.mouse.position;
if (!mousePos) {
return;
}
const canvasSize = container.canvas.size, canvasCenter = {
x: canvasSize.width * half$2,
y: canvasSize.height * half$2,
}, parallaxSmooth = parallaxOptions.smooth, factor = particle.getRadius() / parallaxForce, centerDistance = {
x: (mousePos.x - canvasCenter.x) * factor,
y: (mousePos.y - canvasCenter.y) * factor,
}, { offset } = particle;
offset.x += (centerDistance.x - offset.x) / parallaxSmooth;
offset.y += (centerDistance.y - offset.y) / parallaxSmooth;
}
}
async function loadExternalParallaxInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalParallax", container => {
return Promise.resolve(new Parallaxer(container));
});
});
}
const pauseMode = "pause";
class Pauser extends ExternalInteractorBase {
handleClickMode;
maxDistance = 0;
constructor(container) {
super(container);
this.handleClickMode = (mode) => {
if (mode !== pauseMode) {
return;
}
const container = this.container;
if (container.animationStatus) {
container.pause();
}
else {
container.play();
}
};
}
clear() {
}
init() {
}
interact() {
}
isEnabled(_interactivityData, particle) {
const container = this.container, options = container.actualOptions, events = (particle?.interactivity ?? options.interactivity)?.events;
return !!events && isInArray(pauseMode, events.onClick.mode);
}
reset() {
}
}
async function loadExternalPauseInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalPause", container => {
return Promise.resolve(new Pauser(container));
});
});
}
class Push {
default = true;
groups = [];
particles;
quantity = 4;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "default", data.default);
if (data.groups !== undefined) {
this.groups = data.groups.map(t => t);
}
if (!this.groups.length) {
this.default = true;
}
loadRangeProperty(this, "quantity", data.quantity);
this.particles = executeOnSingleOrMultiple(data.particles, particles => {
return deepExtend({}, particles);
});
}
}
const pushMode = "push", minQuantity = 0;
class Pusher extends ExternalInteractorBase {
handleClickMode;
maxDistance = 0;
constructor(container) {
super(container);
this.handleClickMode = (mode, interactivityData) => {
if (mode !== pushMode) {
return;
}
const container = this.container, options = container.actualOptions, pushOptions = options.interactivity?.modes.push;
if (!pushOptions) {
return;
}
const quantity = getRangeValue(pushOptions.quantity);
if (quantity <= minQuantity) {
return;
}
const group = itemFromArray([undefined, ...pushOptions.groups]), groupOptions = group !== undefined ? container.actualOptions.particles.groups[group] : undefined, particlesOptions = itemFromSingleOrMultiple(pushOptions.particles), overrideOptions = deepExtend(groupOptions, particlesOptions);
container.particles.push(quantity, interactivityData.mouse.position, overrideOptions, group);
};
}
clear() {
}
init() {
}
interact() {
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
return !!events && mouse.clicking && mouse.inside && !!mouse.position && isInArray(pushMode, events.onClick.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "push", Push, ...sources);
}
reset() {
}
}
async function loadExternalPushInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalPush", container => {
return Promise.resolve(new Pusher(container));
});
});
}
class Remove {
quantity = 2;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "quantity", data.quantity);
}
}
const removeMode = "remove";
class Remover extends ExternalInteractorBase {
handleClickMode;
maxDistance = 0;
constructor(container) {
super(container);
this.handleClickMode = (mode) => {
const container = this.container, options = container.actualOptions;
if (!options.interactivity?.modes.remove || mode !== removeMode) {
return;
}
const removeNb = getRangeValue(options.interactivity.modes.remove.quantity);
for (let i = 0; i < removeNb; i++) {
container.particles.get(i)?.destroy();
}
};
}
clear() {
}
init() {
}
interact() {
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
return !!events && mouse.clicking && mouse.inside && !!mouse.position && isInArray(removeMode, events.onClick.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "remove", Remove, ...sources);
}
reset() {
}
}
async function loadExternalRemoveInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalRemove", container => {
return Promise.resolve(new Remover(container));
});
});
}
class RepulseBase {
distance = 200;
duration = 0.4;
easing = "ease-out-quad";
factor = 100;
maxSpeed = 50;
restore;
speed = 1;
constructor() {
this.restore = {
enable: false,
delay: 0,
speed: 0.08,
follow: true,
};
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "distance", data.distance);
loadProperty(this, "duration", data.duration);
loadProperty(this, "easing", data.easing);
loadProperty(this, "factor", data.factor);
loadProperty(this, "speed", data.speed);
loadProperty(this, "maxSpeed", data.maxSpeed);
if (data.restore !== undefined) {
this.restore.enable = data.restore.enable ?? this.restore.enable;
this.restore.delay = data.restore.delay ?? this.restore.delay;
this.restore.speed = data.restore.speed ?? this.restore.speed;
this.restore.follow = data.restore.follow ?? this.restore.follow;
}
}
}
class RepulseDiv extends RepulseBase {
selectors = [];
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "selectors", data.selectors);
}
}
class Repulse extends RepulseBase {
divs;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
this.divs = executeOnSingleOrMultiple(data.divs, div => {
const tmp = new RepulseDiv();
tmp.load(div);
return tmp;
});
}
}
const repulseMode = "repulse", minDistance$2 = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius$4 = 0, minSpeed = 0, easingOffset = 1, minRestoreSpeed = 0.001, maxRestoreSpeed = 1, restoreEpsilon = 0.5;
let Repulser$1 = class Repulser extends ExternalInteractorBase {
handleClickMode;
#clickVec;
#interactedThisFrame;
#maxDistance;
#normVec;
#pluginManager;
#restoreData;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#maxDistance = 0;
this.#normVec = Vector.origin;
this.#interactedThisFrame = new Set();
this.#clickVec = Vector.origin;
this.#restoreData = new Map();
container.repulse ??= { particles: [] };
this.handleClickMode = (mode, interactivityData) => {
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
if (!repulseOpts || mode !== repulseMode) {
return;
}
container.repulse ??= { particles: [] };
const repulse = container.repulse;
repulse.clicking = true;
repulse.count = 0;
for (const particle of container.repulse.particles) {
if (!this.isEnabled(interactivityData, particle)) {
continue;
}
particle.velocity.setTo(particle.initialVelocity);
}
repulse.particles = [];
repulse.finish = false;
setTimeout(() => {
if (container.destroyed) {
return;
}
repulse.clicking = false;
}, repulseOpts.duration * millisecondsToSeconds$1);
};
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
if (!repulse) {
return;
}
this.#maxDistance = repulse.distance;
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
}
interact(interactivityData) {
this.#interactedThisFrame.clear();
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
if (!events) {
return;
}
const hover = events.onHover, hoverEnabled = hover.enable, hoverMode = hover.mode, click = events.onClick, clickEnabled = click.enable, clickMode = click.mode, divs = events.onDiv;
if (mouseMoveStatus && hoverEnabled && isInArray(repulseMode, hoverMode)) {
this.#hoverRepulse(interactivityData);
}
else if (clickEnabled && isInArray(repulseMode, clickMode)) {
this.#clickRepulse(interactivityData);
}
else {
divModeExecute(repulseMode, divs, (selector, div) => {
this.#singleSelectorRepulse(interactivityData, selector, div);
});
}
this.#restoreParticles();
}
isEnabled(interactivityData, particle) {
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
if (!events) {
return false;
}
const divs = events.onDiv, hover = events.onHover, click = events.onClick, divRepulse = isDivModeEnabled(repulseMode, divs);
if (!(divRepulse || (hover.enable && !!mouse.position) || (click.enable && mouse.clickPosition))) {
return false;
}
const hoverMode = hover.mode, clickMode = click.mode;
return isInArray(repulseMode, hoverMode) || isInArray(repulseMode, clickMode) || divRepulse;
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "repulse", Repulse, ...sources);
}
reset() {
}
#clickRepulse(interactivityData) {
const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
if (!repulseOptions) {
return;
}
const repulse = container.repulse ?? { particles: [] };
if (!repulse.finish) {
repulse.count ??= 0;
repulse.count++;
if (repulse.count === container.particles.count) {
repulse.finish = true;
}
}
if (repulse.clicking) {
const repulseDistance = container.retina.repulseModeDistance;
if (!repulseDistance || repulseDistance < minDistance$2) {
return;
}
const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
if (mouseClickPos === undefined) {
return;
}
const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.grid.query(range, p => this.isEnabled(interactivityData, p));
for (const particle of query) {
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
if (d <= repulseRadius) {
this.#trackInteractedParticle(particle);
repulse.particles.push(particle);
this.#clickVec.x = dx;
this.#clickVec.y = dy;
this.#clickVec.length = force;
particle.velocity.setTo(this.#clickVec);
}
}
}
else if (repulse.clicking === false) {
for (const particle of repulse.particles) {
particle.velocity.setTo(particle.initialVelocity);
}
repulse.particles = [];
}
}
#hoverRepulse(interactivityData) {
const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
if (!repulseRadius || repulseRadius < minRadius$4 || !mousePos) {
return;
}
this.#processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
}
#processRepulse(interactivityData, position, repulseRadius, area, divRepulse) {
const container = this.container, query = container.particles.grid.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
if (!repulseOptions) {
return;
}
const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this.#pluginManager.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
for (const particle of query) {
const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
this.#normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
this.#normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
this.#trackInteractedParticle(particle);
particle.position.addTo(this.#normVec);
}
}
#restoreParticles() {
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
if (!restore?.enable || !this.#restoreData.size) {
return;
}
const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds$1, restoreSpeed = Math.max(minRestoreSpeed, Math.min(maxRestoreSpeed, restore.speed));
for (const [particle, restoreData] of this.#restoreData) {
if (this.#interactedThisFrame.has(particle)) {
continue;
}
if (particle.destroyed) {
this.#restoreData.delete(particle);
continue;
}
const target = restoreData.target;
if (now - restoreData.lastInteractionTime < restoreDelay) {
continue;
}
if (restore.follow && particle.options.move.enable) {
target.x += particle.velocity.x;
target.y += particle.velocity.y;
target.z += particle.velocity.z;
}
const dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
particle.position.x += dx * restoreSpeed;
particle.position.y += dy * restoreSpeed;
particle.position.z += dz * restoreSpeed;
if (Math.abs(dx) <= restoreEpsilon && Math.abs(dy) <= restoreEpsilon) {
particle.position.x = target.x;
particle.position.y = target.y;
particle.position.z = target.z;
this.#restoreData.delete(particle);
continue;
}
}
}
#singleSelectorRepulse(interactivityData, selector, div) {
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
if (!repulse) {
return;
}
const query = safeDocument().querySelectorAll(selector);
if (!query.length) {
return;
}
query.forEach(item => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth * half$2) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight * half$2) * pxRatio,
}, repulseRadius = elem.offsetWidth * half$2 * pxRatio, area = div.type === DivType.circle
? new Circle(pos.x, pos.y, repulseRadius)
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
this.#processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
});
}
#trackInteractedParticle(particle) {
this.#interactedThisFrame.add(particle);
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
if (!restore?.enable) {
return;
}
const now = Date.now();
let restoreData = this.#restoreData.get(particle);
if (!restoreData) {
restoreData = {
target: particle.position.copy(),
lastInteractionTime: now,
};
this.#restoreData.set(particle, restoreData);
}
restoreData.lastInteractionTime = now;
if (restore.follow && particle.options.move.enable) {
restoreData.target.x += particle.velocity.x;
restoreData.target.y += particle.velocity.y;
restoreData.target.z += particle.velocity.z;
}
}
};
async function loadExternalRepulseInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
const pluginManager = e.pluginManager;
pluginManager.addInteractor?.("externalRepulse", container => {
return Promise.resolve(new Repulser$1(pluginManager, container));
});
});
}
class Slow {
factor = 3;
radius = 200;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "factor", data.factor);
loadProperty(this, "radius", data.radius);
}
}
class SlowModifier {
enabled = false;
id = "slow";
priority = 100;
speedFactor = 1;
}
const slowMode = "slow", minRadius$3 = 0;
class Slower extends ExternalInteractorBase {
#maxDistance;
#modifiers = new WeakMap();
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear(particle, _delta, force) {
const mod = this.#modifiers.get(particle);
if (mod?.enabled && !force) {
return;
}
particle.removeModifier(slowMode);
this.#modifiers.delete(particle);
}
getOrCreateModifier(particle) {
let mod = this.#modifiers.get(particle);
if (!mod) {
mod = new SlowModifier();
this.#modifiers.set(particle, mod);
particle.addModifier(mod);
}
return mod;
}
init() {
const container = this.container, slow = container.actualOptions.interactivity?.modes.slow;
if (!slow) {
return;
}
this.#maxDistance = slow.radius;
container.retina.slowModeRadius = slow.radius * container.retina.pixelRatio;
}
interact() {
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, events = (particle?.interactivity ?? container.actualOptions.interactivity)?.events;
return !!events?.onHover.enable && !!mouse.position && isInArray(slowMode, events.onHover.mode);
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "slow", Slow, ...sources);
}
reset(interactivityData, particle) {
const mod = this.#modifiers.get(particle);
if (mod) {
mod.enabled = false;
}
const container = this.container, options = container.actualOptions, mousePos = interactivityData.mouse.position, radius = container.retina.slowModeRadius, slowOptions = options.interactivity?.modes.slow;
if (!slowOptions || !radius || radius < minRadius$3 || !mousePos) {
return;
}
const particlePos = particle.getPosition(), dist = getDistance(mousePos, particlePos), proximityFactor = dist / radius, slowFactor = slowOptions.factor;
if (dist > radius) {
return;
}
const activeMod = this.getOrCreateModifier(particle);
activeMod.enabled = true;
activeMod.speedFactor = proximityFactor / slowFactor;
}
}
async function loadExternalSlowInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalSlow", container => {
return Promise.resolve(new Slower(container));
});
});
}
const shapeTypes = ["image", "images"];
const stringStart = 0, defaultOpacity = 1;
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;
function replaceColorSvg(imageShape, color, opacity, hdr = false, peakNits, mode) {
const { svgData } = imageShape;
if (!svgData) {
return "";
}
const colorStyle = getStyleFromHsl(color, hdr, opacity, peakNits, mode);
if (svgData.includes("fill")) {
return svgData.replaceAll(currentColorRegex, () => colorStyle);
}
const preFillIndex = svgData.indexOf(">");
return `${svgData.substring(stringStart, preFillIndex)} fill="${colorStyle}"${svgData.substring(preFillIndex)}`;
}
async function loadImage(image) {
return new Promise(resolve => {
image.loading = true;
const img = new Image();
image.element = img;
img.addEventListener("load", () => {
image.loading = false;
resolve();
});
img.addEventListener("error", () => {
image.element = undefined;
image.error = true;
image.loading = false;
getLogger().error(`Error loading image: ${image.source}`);
resolve();
});
img.src = image.source;
});
}
async function downloadSvgImage(image) {
if (image.type !== "svg") {
await loadImage(image);
return;
}
image.loading = true;
const response = await fetch(image.source);
if (response.ok) {
image.svgData = await response.text();
}
else {
getLogger().error("Image not found");
image.error = true;
}
image.loading = false;
}
function replaceImageColor(image, imageData, color, particle, hdr = false, peakNits, mode) {
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? defaultOpacity, hdr, peakNits, mode), imageRes = {
color,
data: {
...image,
svgData: svgColoredData,
},
loaded: false,
ratio: imageData.width / imageData.height,
replaceColor: imageData.replaceColor,
source: imageData.src,
};
return new Promise(resolve => {
const svg = new Blob([svgColoredData], { type: "image/svg+xml" }), url = URL.createObjectURL(svg), img = new Image();
img.addEventListener("load", () => {
imageRes.loaded = true;
imageRes.element = img;
resolve(imageRes);
URL.revokeObjectURL(url);
});
const errorHandler = async () => {
URL.revokeObjectURL(url);
const img2 = {
...image,
error: false,
loading: true,
};
await loadImage(img2);
imageRes.loaded = true;
imageRes.element = img2.element;
resolve(imageRes);
};
img.addEventListener("error", () => void errorHandler());
img.src = url;
});
}
const sides$3 = 12;
class ImageDrawer {
#container;
#engine;
constructor(engine, container) {
this.#engine = engine;
this.#container = container;
}
draw(data) {
const { context, radius, particle, opacity } = data, image = particle.image, element = image?.element;
if (!image) {
return;
}
context.globalAlpha = opacity;
if (element) {
const ratio = image.ratio, pos = {
x: -radius,
y: -radius,
}, diameter = radius * double;
context.drawImage(element, pos.x, pos.y, diameter, diameter / ratio);
}
context.globalAlpha = defaultAlpha$1;
}
getSidesCount() {
return sides$3;
}
async init(container) {
const options = container.actualOptions;
if (!options.preload || !this.#engine.loadImage) {
return;
}
const promises = [];
for (const imageData of options.preload) {
promises.push(this.#engine.loadImage(container, imageData));
}
await Promise.all(promises);
}
loadShape(particle) {
const container = this.#container;
if (!particle.shape || !shapeTypes.includes(particle.shape)) {
return;
}
const imageData = particle.shapeData;
if (!imageData) {
return;
}
const images = this.#engine.getImages?.(container), image = images?.find((t) => t.name === imageData.name || t.source === imageData.src);
if (image) {
return;
}
void this.#loadImageShape(container, imageData).then(() => {
this.loadShape(particle);
});
}
particleInit(container, particle) {
if (particle.shape !== "image" && particle.shape !== "images") {
return;
}
const images = this.#engine.getImages?.(container), imageData = particle.shapeData;
if (!imageData) {
return;
}
const color = particle.getFillColor(), image = images?.find((t) => t.name === imageData.name || t.source === imageData.src);
if (!image) {
return;
}
const replaceColor = imageData.replaceColor;
if (image.loading) {
setTimeout(() => {
this.particleInit(container, particle);
});
return;
}
void (async () => {
let imageRes;
if (image.svgData && color) {
imageRes = await replaceImageColor(image, imageData, color, particle, container.hdr, container.peakNits, container.hdrMode);
}
else {
imageRes = {
color,
data: image,
element: image.element,
loaded: true,
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : (image.ratio ?? defaultRatio$1),
replaceColor: replaceColor,
source: imageData.src,
};
}
if (!imageRes.ratio) {
imageRes.ratio = 1;
}
const close = imageData.close ?? particle.shapeClose, imageShape = {
image: imageRes,
close,
};
particle.image = imageShape.image;
particle.shapeClose = imageShape.close;
})();
}
#loadImageShape = async (container, imageShape) => {
if (!this.#engine.loadImage) {
throw new Error(`Image shape not initialized`);
}
await this.#engine.loadImage(container, {
name: imageShape.name,
replaceColor: imageShape.replaceColor,
src: imageShape.src,
});
};
}
class Preload {
height;
name;
replaceColor;
src = "";
width;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "height", data.height);
loadProperty(this, "name", data.name);
loadProperty(this, "replaceColor", data.replaceColor);
loadProperty(this, "src", data.src);
loadProperty(this, "width", data.width);
}
}
class ImagePreloaderPlugin {
id = "image-preloader";
#engine;
constructor(engine) {
this.#engine = engine;
}
async getPlugin(container) {
const { ImagePreloaderInstance } = await Promise.resolve().then(function () { return ImagePreloaderInstance$1; });
return new ImagePreloaderInstance(this.#engine, container);
}
loadOptions(_container, options, source) {
if (!source?.preload) {
return;
}
options.preload ??= [];
const preloadOptions = options.preload;
for (const item of source.preload) {
const existing = preloadOptions.find(t => t.name === item.name || t.src === item.src);
if (existing) {
existing.load(item);
}
else {
const preload = new Preload();
preload.load(item);
preloadOptions.push(preload);
}
}
}
needsPlugin() {
return true;
}
}
const extLength = 3;
function addLoadImageToEngine(engine) {
engine.getImages ??= (container) => {
engine.images ??= new Map();
let images = engine.images.get(container);
if (!images) {
images = [];
engine.images.set(container, images);
}
return images;
};
engine.loadImage ??= async (container, data) => {
if (!engine.getImages) {
throw new Error("No images collection found");
}
if (!data.name && !data.src) {
throw new Error("No image source provided");
}
engine.images ??= new Map();
const containerImages = engine.getImages(container);
if (containerImages.some((t) => t.name === data.name || t.source === data.src)) {
return;
}
try {
const image = {
name: data.name ?? data.src,
source: data.src,
type: data.src.substring(data.src.length - extLength),
error: false,
loading: true,
replaceColor: data.replaceColor,
ratio: data.width && data.height ? data.width / data.height : undefined,
};
containerImages.push(image);
engine.images.set(container, containerImages);
let imageFunc;
if (data.replaceColor) {
imageFunc = downloadSvgImage;
}
else {
imageFunc = loadImage;
}
await imageFunc(image);
}
catch {
throw new Error(`${data.name ?? data.src} not found`);
}
};
}
async function loadImageShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
addLoadImageToEngine(e);
e.pluginManager.addPlugin(new ImagePreloaderPlugin(e));
e.pluginManager.addShape(shapeTypes, container => Promise.resolve(new ImageDrawer(e, container)));
});
}
class LifeDelay extends ValueWithRandom {
sync = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "sync", data.sync);
}
}
class LifeDuration extends ValueWithRandom {
sync = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "sync", data.sync);
}
}
class Life {
count = 0;
delay = new LifeDelay();
duration = new LifeDuration();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "count", data.count);
this.delay.load(data.delay);
this.duration.load(data.duration);
}
}
const noTime$1 = 0, infiniteValue$1 = -1, noLife = 0, minCanvasSize = 0;
function updateLife(particle, delta, canvasSize) {
if (!particle.life) {
return;
}
const life = particle.life;
let justSpawned = false;
if (particle.spawning) {
life.delayTime += delta.value;
if (life.delayTime >= particle.life.delay) {
justSpawned = true;
particle.spawning = false;
life.delayTime = noTime$1;
life.time = noTime$1;
}
else {
return;
}
}
if (life.duration === infiniteValue$1) {
return;
}
if (justSpawned) {
life.time = noTime$1;
}
else {
life.time += delta.value;
}
if (life.time < life.duration) {
return;
}
life.time = noTime$1;
if (particle.life.count > noLife) {
particle.life.count--;
}
if (particle.life.count === noLife) {
particle.destroy();
return;
}
const widthRange = setRangeValue(minCanvasSize, canvasSize.width), heightRange = setRangeValue(minCanvasSize, canvasSize.width);
particle.position.x = randomInRangeValue(widthRange);
particle.position.y = randomInRangeValue(heightRange);
particle.spawning = true;
life.delayTime = noTime$1;
life.time = noTime$1;
particle.reset();
const lifeOptions = particle.options.life;
if (lifeOptions) {
life.delay = getRangeValue(lifeOptions.delay.value) * millisecondsToSeconds$1;
life.duration = getRangeValue(lifeOptions.duration.value) * millisecondsToSeconds$1;
}
}
const noTime = 0, identity$2 = 1, infiniteValue = -1;
class LifeUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const container = this.#container, particlesOptions = particle.options, lifeOptions = particlesOptions.life;
if (!lifeOptions) {
return;
}
const delayFactor = lifeOptions.delay.sync ? identity$2 : getRandom(), durationFactor = lifeOptions.duration.sync ? identity$2 : getRandom();
particle.life = {
delay: container.retina.reduceFactor
? ((getRangeValue(lifeOptions.delay.value) * delayFactor) / container.retina.reduceFactor) *
millisecondsToSeconds$1
: noTime,
delayTime: noTime,
duration: container.retina.reduceFactor
? ((getRangeValue(lifeOptions.duration.value) * durationFactor) / container.retina.reduceFactor) *
millisecondsToSeconds$1
: noTime,
time: noTime,
count: lifeOptions.count,
};
if (particle.life.duration <= noTime) {
particle.life.duration = infiniteValue;
}
if (particle.life.count <= noTime) {
particle.life.count = infiniteValue;
}
particle.spawning = particle.life.delay > noTime;
}
isEnabled(particle) {
return !particle.destroyed;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "life", Life, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.life) {
return;
}
updateLife(particle, delta, this.#container.canvas.size);
}
}
async function loadLifeUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("life", container => {
return Promise.resolve(new LifeUpdater(container));
});
});
}
function drawLine(data) {
const { context, particle, radius } = data, shapeData = particle.shapeData, centerY = 0;
context.moveTo(-radius, centerY);
context.lineTo(radius, centerY);
context.lineCap = shapeData?.cap ?? "butt";
}
const sides$2 = 1;
class LineDrawer {
draw(data) {
drawLine(data);
}
getSidesCount() {
return sides$2;
}
}
async function loadLineShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["line"], () => Promise.resolve(new LineDrawer()));
});
}
class Attract {
distance = 200;
enable = false;
rotate;
constructor() {
this.rotate = {
x: 3000,
y: 3000,
};
}
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "distance", data.distance);
loadProperty(this, "enable", data.enable);
if (data.rotate) {
const rotateX = data.rotate.x;
if (rotateX !== undefined) {
this.rotate.x = rotateX;
}
const rotateY = data.rotate.y;
if (rotateY !== undefined) {
this.rotate.y = rotateY;
}
}
}
}
const attractFactor = 1000, identity$1 = 1;
class Attractor extends ParticlesInteractorBase {
#maxDistance;
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
}
interact(p1) {
if (!p1.options.attract?.enable) {
return;
}
const container = this.container;
if (isNull(p1.attractDistance)) {
const attractDistance = getRangeValue(p1.options.attract.distance);
if (attractDistance > this.#maxDistance) {
this.#maxDistance = attractDistance;
}
p1.attractDistance = attractDistance * container.retina.pixelRatio;
}
const distance = p1.attractDistance, pos1 = p1.getPosition(), query = container.particles.grid.queryCircle(pos1, distance);
for (const p2 of query) {
if (p1 === p2 || !p2.options.attract?.enable || p2.destroyed || p2.spawning) {
continue;
}
const pos2 = p2.getPosition(), { dx, dy } = getDistances(pos1, pos2), rotate = p1.options.attract.rotate, ax = dx / (rotate.x * attractFactor), ay = dy / (rotate.y * attractFactor), p1Factor = p2.size.value / p1.size.value, p2Factor = identity$1 / p1Factor;
p1.velocity.x -= ax * p1Factor;
p1.velocity.y -= ay * p1Factor;
p2.velocity.x += ax * p2Factor;
p2.velocity.y += ay * p2Factor;
}
}
isEnabled(particle) {
return particle.options.attract?.enable ?? false;
}
loadParticlesOptions(options, ...sources) {
loadOptionProperty(options, "attract", Attract, ...sources);
}
reset() {
}
}
async function loadParticlesAttractInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("particlesAttract", container => {
return Promise.resolve(new Attractor(container));
});
});
}
var CollisionMode;
(function (CollisionMode) {
CollisionMode["absorb"] = "absorb";
CollisionMode["bounce"] = "bounce";
CollisionMode["destroy"] = "destroy";
})(CollisionMode || (CollisionMode = {}));
class CollisionsAbsorb {
speed = 2;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "speed", data.speed);
}
}
class CollisionsOverlap {
enable = true;
retries = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "retries", data.retries);
}
}
class Collisions {
absorb = new CollisionsAbsorb();
bounce = new ParticlesBounce();
enable = false;
maxSpeed = 50;
mode = CollisionMode.bounce;
overlap = new CollisionsOverlap();
load(data) {
if (isNull(data)) {
return;
}
this.absorb.load(data.absorb);
this.bounce.load(data.bounce);
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "maxSpeed", data.maxSpeed);
loadProperty(this, "mode", data.mode);
this.overlap.load(data.overlap);
}
}
const minAbsorbAmount = 0;
function updateAbsorb(p1, r1, p2, r2, delta, pixelRatio) {
if (!p1.options.collisions || !p2.options.collisions) {
return;
}
const absorbSpeed = p1.options.collisions.absorb.speed, shrinkAmount = clamp(absorbSpeed * delta.factor, minAbsorbAmount, r2);
p1.size.value = Math.sqrt(r1 * r1 + shrinkAmount * shrinkAmount);
p2.size.value -= shrinkAmount;
if (p2.size.value <= pixelRatio) {
p2.size.value = 0;
p2.destroy();
}
}
function absorb(p1, p2, delta, pixelRatio) {
const r1 = p1.getRadius(), r2 = p2.getRadius();
if (!r1 && r2) {
p1.destroy();
}
else if (r1 && !r2) {
p2.destroy();
}
else if (r1 && r2) {
if (r1 >= r2) {
updateAbsorb(p1, r1, p2, r2, delta, pixelRatio);
}
else {
updateAbsorb(p2, r2, p1, r1, delta, pixelRatio);
}
}
}
const energyCorrectionMinRatio = 1e-6, energyDriftThreshold = 1e-4, correctionFactorNeutral = 1, fixBounceSpeed = (p) => {
if (!p.options.collisions) {
return;
}
p.collisionMaxSpeed ??= getRangeValue(p.options.collisions.maxSpeed);
if (p.velocity.length > p.collisionMaxSpeed) {
p.velocity.length = p.collisionMaxSpeed;
}
};
function bounce(p1, p2) {
const m1 = p1.getMass(), m2 = p2.getMass(), speed1Before = p1.velocity.length, speed2Before = p2.velocity.length, keBefore = m1 * speed1Before * speed1Before + m2 * speed2Before * speed2Before;
circleBounce(circleBounceDataFromParticle(p1), circleBounceDataFromParticle(p2));
const speed1After = p1.velocity.length, speed2After = p2.velocity.length, keAfter = m1 * speed1After * speed1After + m2 * speed2After * speed2After;
if (keAfter > keBefore * energyCorrectionMinRatio) {
const correctionFactor = Math.sqrt(keBefore / keAfter);
if (Math.abs(correctionFactor - correctionFactorNeutral) > energyDriftThreshold) {
p1.velocity.length = speed1After * correctionFactor;
p2.velocity.length = speed2After * correctionFactor;
}
}
fixBounceSpeed(p1);
fixBounceSpeed(p2);
}
function destroy(p1, p2) {
if (!p1.unbreakable && !p2.unbreakable) {
bounce(p1, p2);
}
const p1Radius = p1.getRadius(), p2Radius = p2.getRadius();
if (!p1Radius && p2Radius) {
p1.destroy();
}
else if (p1Radius && !p2Radius) {
p2.destroy();
}
else if (p1Radius && p2Radius) {
const deleteP = p1.getRadius() >= p2.getRadius() ? p2 : p1;
deleteP.destroy();
}
}
function resolveCollision(p1, p2, delta, pixelRatio) {
if (!p1.options.collisions || !p2.options.collisions) {
return;
}
switch (p1.options.collisions.mode) {
case CollisionMode.absorb: {
absorb(p1, p2, delta, pixelRatio);
break;
}
case CollisionMode.bounce: {
bounce(p1, p2);
break;
}
case CollisionMode.destroy: {
destroy(p1, p2);
break;
}
}
}
class Collider extends ParticlesInteractorBase {
maxDistance;
constructor(container) {
super(container);
this.maxDistance = 0;
}
clear() {
}
init() {
}
interact(p1, _interactivityData, delta) {
if (p1.destroyed || p1.spawning) {
return;
}
const container = this.container, pos1 = p1.getPosition(), radius1 = p1.getRadius(), query = container.particles.grid.queryCircle(pos1, radius1 * double);
for (const p2 of query) {
if (p1 === p2 ||
p1.id >= p2.id ||
!p1.options.collisions?.enable ||
!p2.options.collisions?.enable ||
p1.options.collisions.mode !== p2.options.collisions.mode ||
p2.destroyed ||
p2.spawning) {
continue;
}
const pos2 = p2.getPosition(), radius2 = p2.getRadius();
if (Math.abs(Math.round(pos1.z) - Math.round(pos2.z)) > radius1 + radius2) {
continue;
}
const dist = getDistance(pos1, pos2), distP = radius1 + radius2;
if (dist > distP) {
continue;
}
resolveCollision(p1, p2, delta, container.retina.pixelRatio);
}
}
isEnabled(particle) {
return !!particle.options.collisions?.enable;
}
loadParticlesOptions(options, ...sources) {
loadOptionProperty(options, "collisions", Collisions, ...sources);
}
reset() {
}
}
class OverlapPlugin {
id = "overlap";
async getPlugin(container) {
const { OverlapPluginInstance } = await Promise.resolve().then(function () { return OverlapPluginInstance$1; });
return new OverlapPluginInstance(container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadParticlesCollisionsInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addPlugin(new OverlapPlugin());
e.pluginManager.addInteractor?.("particlesCollisions", container => {
return Promise.resolve(new Collider(container));
});
});
}
class CircleWarp extends Circle {
#canvasSize;
constructor(x, y, radius, canvasSize) {
super(x, y, radius);
this.#canvasSize = canvasSize;
}
contains(point) {
if (super.contains(point))
return true;
const { width, height } = this.#canvasSize, { x, y } = point;
return (super.contains({ x: x - width, y }) ||
super.contains({ x: x + width, y }) ||
super.contains({ x, y: y - height }) ||
super.contains({ x, y: y + height }) ||
super.contains({ x: x - width, y: y - height }) ||
super.contains({ x: x + width, y: y + height }) ||
super.contains({ x: x - width, y: y + height }) ||
super.contains({ x: x + width, y: y - height }));
}
intersects(range) {
if (super.intersects(range))
return true;
const { width, height } = this.#canvasSize, pos = range.position, shifts = [
{ x: -width, y: 0 },
{ x: width, y: 0 },
{ x: 0, y: -height },
{ x: 0, y: height },
{ x: -width, y: -height },
{ x: width, y: height },
{ x: -width, y: height },
{ x: width, y: -height },
];
for (const shift of shifts) {
const shiftedPos = { x: pos.x + shift.x, y: pos.y + shift.y };
let shiftedRange;
if (range instanceof Circle) {
shiftedRange = new Circle(shiftedPos.x, shiftedPos.y, range.radius);
}
else {
const rect = range;
shiftedRange = new Rectangle(shiftedPos.x, shiftedPos.y, rect.size.width, rect.size.height);
}
if (super.intersects(shiftedRange))
return true;
}
return false;
}
}
class LinksShadow {
blur = 5;
color = new OptionsColor();
enable = false;
constructor() {
this.color.value = "#000";
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "blur", data.blur);
this.color = OptionsColor.create(this.color, data.color);
loadProperty(this, "enable", data.enable);
}
}
class LinksTriangle {
color;
enable = false;
frequency = 1;
opacity;
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "frequency", data.frequency);
loadProperty(this, "opacity", data.opacity);
}
}
class Links {
blink = false;
color = new OptionsColor();
consent = false;
distance = 100;
enable = false;
frequency = 1;
id;
opacity = 1;
shadow = new LinksShadow();
triangles = new LinksTriangle();
warp = false;
width = 1;
constructor() {
this.color.value = "#fff";
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "id", data.id);
loadProperty(this, "blink", data.blink);
this.color = OptionsColor.create(this.color, data.color);
loadProperty(this, "consent", data.consent);
loadProperty(this, "distance", data.distance);
loadProperty(this, "enable", data.enable);
loadProperty(this, "frequency", data.frequency);
loadProperty(this, "opacity", data.opacity);
this.shadow.load(data.shadow);
this.triangles.load(data.triangles);
loadProperty(this, "width", data.width);
loadProperty(this, "warp", data.warp);
}
}
const opacityOffset = 1, minDistance$1 = 0;
function getWarpDistance(pos1, pos2, canvasSize) {
const { dx, dy } = getDistances(pos1, pos2), absDiffs = { x: Math.abs(dx), y: Math.abs(dy) }, warpDistances = {
x: Math.min(absDiffs.x, canvasSize.width - absDiffs.x),
y: Math.min(absDiffs.y, canvasSize.height - absDiffs.y),
};
return Math.hypot(warpDistances.x, warpDistances.y);
}
class Linker extends ParticlesInteractorBase {
#maxDistance;
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
this.container.particles.linksColor = undefined;
this.container.particles.linksColors = new Map();
}
interact(p1) {
if (!p1.options.links) {
return;
}
p1.links = [];
if (p1.linksDistance && p1.linksDistance > this.#maxDistance) {
this.#maxDistance = p1.linksDistance;
}
const pos1 = p1.getPosition(), container = this.container, canvasSize = container.canvas.size;
if (pos1.x < originPoint.x || pos1.y < originPoint.y || pos1.x > canvasSize.width || pos1.y > canvasSize.height) {
return;
}
const linkOpt1 = p1.options.links, optOpacity = linkOpt1.opacity, optDistance = p1.retina.linksDistance ?? minDistance$1, warp = linkOpt1.warp, range = warp ? new CircleWarp(pos1.x, pos1.y, optDistance, canvasSize) : new Circle(pos1.x, pos1.y, optDistance), query = container.particles.grid.query(range);
for (const p2 of query) {
const linkOpt2 = p2.options.links;
if (p1 === p2 ||
!linkOpt2?.enable ||
linkOpt1.id !== linkOpt2.id ||
p2.spawning ||
p2.destroyed ||
!p2.links ||
p1.links.some(t => t.destination === p2) ||
p2.links.some(t => t.destination === p1)) {
continue;
}
const pos2 = p2.getPosition();
if (pos2.x < originPoint.x || pos2.y < originPoint.y || pos2.x > canvasSize.width || pos2.y > canvasSize.height) {
continue;
}
const distDirect = getDistances(pos1, pos2).distance, distWarp = warp && linkOpt2.warp ? getWarpDistance(pos1, pos2, canvasSize) : distDirect, distance = Math.min(distDirect, distWarp);
if (distance > optDistance) {
continue;
}
const opacityLine = (opacityOffset - distance / optDistance) * optOpacity;
this.#setColor(p1);
p1.links.push({
destination: p2,
opacity: opacityLine,
color: this.#getLinkColor(p1, p2),
isWarped: distWarp < distDirect,
});
}
}
isEnabled(particle) {
return !!particle.options.links?.enable;
}
loadParticlesOptions(options, ...sources) {
loadOptionProperty(options, "links", Links, ...sources);
}
reset() {
}
#getLinkColor(p1, p2) {
const container = this.container, linksOptions = p1.options.links;
if (!linksOptions) {
return;
}
const linkColor = linksOptions.id !== undefined
? container.particles.linksColors.get(linksOptions.id)
: container.particles.linksColor;
return getLinkColor(p1, p2, linkColor, container.hdr);
}
#setColor(p1) {
if (!p1.options.links) {
return;
}
const container = this.container, linksOptions = p1.options.links;
let linkColor = linksOptions.id === undefined
? container.particles.linksColor
: container.particles.linksColors.get(linksOptions.id);
if (linkColor) {
return;
}
linkColor = getLinkRandomColor(this.#pluginManager, linksOptions.color, linksOptions.blink, linksOptions.consent);
if (linksOptions.id === undefined) {
container.particles.linksColor = linkColor;
}
else {
container.particles.linksColors.set(linksOptions.id, linkColor);
}
}
}
class LinksPlugin {
id = "links";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { LinkInstance } = await Promise.resolve().then(function () { return LinkInstance$1; });
return new LinkInstance(this.#pluginManager, container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadParticlesLinksInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
const pluginManager = e.pluginManager;
ensureInteractivityPluginLoaded(e);
pluginManager.addPlugin(new LinksPlugin(pluginManager));
pluginManager.addInteractor?.("particlesLinks", container => {
return Promise.resolve(new Linker(pluginManager, container));
});
});
}
const polygonCache = new Map(), noOffset = 0;
function getUnitPolygon(sides) {
const cached = polygonCache.get(sides);
if (cached) {
return cached;
}
const step = doublePI / sides, isOdd = !!(sides % double), baseAngle = (-Math.PI + (isOdd ? noOffset : step)) * half$2, verts = [];
for (let i = 0; i < sides; i++) {
const angle = baseAngle + i * step;
verts[i] = {
x: Math.cos(angle),
y: Math.sin(angle),
};
}
polygonCache.set(sides, verts);
return verts;
}
function drawPolygon(data, side) {
const { context, radius } = data, sides = side.count.numerator / side.count.denominator, verts = getUnitPolygon(sides);
context.beginPath();
for (let i = 0; i < verts.length; i++) {
const vert = verts[i];
if (!vert) {
continue;
}
const x = vert.x * radius, y = vert.y * radius;
if (i) {
context.lineTo(x, y);
}
else {
context.moveTo(x, y);
}
}
context.closePath();
}
const defaultSides$3 = 5;
class PolygonDrawerBase {
draw(data) {
const { particle, radius } = data, side = this.getSidesData(particle, radius);
drawPolygon(data, side);
}
getSidesCount(particle) {
const polygon = particle.shapeData;
return Math.round(getRangeValue(polygon?.sides ?? defaultSides$3));
}
}
const yFactor$1 = 2.66, sidesFactor$1 = 3;
class PolygonDrawer extends PolygonDrawerBase {
getSidesData(particle, radius) {
const { sides } = particle;
return {
count: {
denominator: 1,
numerator: sides,
},
length: (radius * yFactor$1) / (sides / sidesFactor$1),
};
}
}
const sides$1 = 3, yFactor = 2.66, sidesFactor = 3;
class TriangleDrawer extends PolygonDrawerBase {
getSidesCount() {
return sides$1;
}
getSidesData(_particle, radius) {
return {
count: {
denominator: 1,
numerator: sides$1,
},
length: (radius * yFactor) / (sides$1 / sidesFactor),
};
}
}
async function loadGenericPolygonShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["polygon"], () => Promise.resolve(new PolygonDrawer()));
});
}
async function loadTriangleShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["triangle"], () => Promise.resolve(new TriangleDrawer()));
});
}
async function loadPolygonShape(engine) {
engine.checkVersion("4.4.0");
await Promise.all([
loadGenericPolygonShape(engine),
loadTriangleShape(engine),
]);
}
class RotateAnimation {
decay = 0;
enable = false;
speed = 0;
sync = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "decay", data.decay);
loadProperty(this, "sync", data.sync);
}
}
class Rotate extends ValueWithRandom {
animation = new RotateAnimation();
direction = exports.RotateDirection.clockwise;
path = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "direction", data.direction);
this.animation.load(data.animation);
loadProperty(this, "path", data.path);
}
}
const doublePIDeg = 360;
class RotateUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const rotateOptions = particle.options.rotate;
if (!rotateOptions) {
return;
}
particle.rotate = {
enable: rotateOptions.animation.enable,
value: degToRad(getRangeValue(rotateOptions.value)),
min: 0,
max: doublePI,
};
particle.pathRotation = rotateOptions.path;
let rotateDirection = rotateOptions.direction;
if (rotateDirection === exports.RotateDirection.random) {
const index = Math.floor(getRandom() * double), minIndex = 0;
rotateDirection = index > minIndex ? exports.RotateDirection.counterClockwise : exports.RotateDirection.clockwise;
}
switch (rotateDirection) {
case exports.RotateDirection.counterClockwise:
case "counterClockwise":
particle.rotate.status = exports.AnimationStatus.decreasing;
break;
case exports.RotateDirection.clockwise:
particle.rotate.status = exports.AnimationStatus.increasing;
break;
}
const rotateAnimation = rotateOptions.animation;
if (rotateAnimation.enable) {
particle.rotate.decay = identity$3 - getRangeValue(rotateAnimation.decay);
particle.rotate.velocity =
(getRangeValue(rotateAnimation.speed) / doublePIDeg) * this.#container.retina.reduceFactor;
if (!rotateAnimation.sync) {
particle.rotate.velocity *= getRandom();
}
}
particle.rotation = particle.rotate.value;
}
isEnabled(particle) {
const rotate = particle.options.rotate;
if (!rotate) {
return false;
}
return !particle.destroyed && !particle.spawning && (!!rotate.value || rotate.animation.enable || rotate.path);
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "rotate", Rotate, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
particle.isRotating = !!particle.rotate;
if (!particle.rotate) {
return;
}
updateAnimation(particle, particle.rotate, false, exports.DestroyType.none, delta);
particle.rotation = particle.rotate.value;
}
}
async function loadRotateUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("rotate", container => {
return Promise.resolve(new RotateUpdater(container));
});
});
}
function drawSquare(data) {
const { context, radius } = data, fixedRadius = radius * Math.SQRT1_2, fixedDiameter = fixedRadius * double;
context.rect(-fixedRadius, -fixedRadius, fixedDiameter, fixedDiameter);
}
const sides = 4;
class SquareDrawer {
draw(data) {
drawSquare(data);
}
getSidesCount() {
return sides;
}
}
async function loadSquareShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["edge", "square"], () => Promise.resolve(new SquareDrawer()));
});
}
const defaultInset$1 = 2, origin = { x: 0, y: 0 };
function drawStar(data) {
const { context, particle, radius } = data, sides = particle.sides, inset = particle.starInset ?? defaultInset$1;
context.moveTo(origin.x, origin.y - radius);
for (let i = 0; i < sides; i++) {
context.rotate(Math.PI / sides);
context.lineTo(origin.x, origin.y - radius * inset);
context.rotate(Math.PI / sides);
context.lineTo(origin.x, origin.y - radius);
}
}
const defaultInset = 2, defaultSides$2 = 5;
class StarDrawer {
draw(data) {
drawStar(data);
}
getSidesCount(particle) {
const star = particle.shapeData;
return Math.round(getRangeValue(star?.sides ?? defaultSides$2));
}
particleInit(_container, particle) {
const star = particle.shapeData;
particle.starInset = getRangeValue(star?.inset ?? defaultInset);
}
}
async function loadStarShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["star"], () => Promise.resolve(new StarDrawer()));
});
}
async function loadSlim(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const loadInteractivityForSlim = async (e) => {
await loadInteractivityPlugin(e);
await Promise.all([
loadExternalParallaxInteraction(e),
loadExternalAttractInteraction(e),
loadExternalBounceInteraction(e),
loadExternalBubbleInteraction(e),
loadExternalConnectInteraction(e),
loadExternalDestroyInteraction(e),
loadExternalGrabInteraction(e),
loadExternalPauseInteraction(e),
loadExternalPushInteraction(e),
loadExternalRemoveInteraction(e),
loadExternalRepulseInteraction(e),
loadExternalSlowInteraction(e),
loadParticlesAttractInteraction(e),
loadParticlesCollisionsInteraction(e),
loadParticlesLinksInteraction(e),
]);
};
await Promise.all([
loadBasic(e),
loadInteractivityForSlim(e),
loadEasingQuadPlugin(e),
loadEmojiShape(e),
loadImageShape(e),
loadLineShape(e),
loadPolygonShape(e),
loadSquareShape(e),
loadStarShape(e),
loadLifeUpdater(e),
loadPaintUpdater(e),
loadRotateUpdater(e),
]);
});
}
const validTypes = ["text", "character", "char", "multiline-text"];
const firstIndex$3 = 0, minLength$2 = 0;
function drawText(data) {
const { context, particle, fill, stroke, radius, opacity } = data, character = particle.shapeData;
if (!character) {
return;
}
const textData = character.value;
particle.textLines ??= itemFromSingleOrMultiple(textData, particle.randomIndexData)?.split("\n") ?? [];
particle.maxTextLength ??= particle.textLines.length
? Math.max(...particle.textLines.map(t => t.length))
: (particle.textLines[firstIndex$3]?.length ?? minLength$2);
if (!particle.textLines.length || !particle.maxTextLength) {
return;
}
const lines = particle.textLines, style = character.style ?? "", weight = character.weight ?? "400", font = character.font ?? "Verdana", size = (Math.round(radius) * double) / (lines.length * particle.maxTextLength);
context.font = `${style} ${weight} ${size.toString()}px "${font}"`;
const originalGlobalAlpha = context.globalAlpha;
context.globalAlpha = opacity;
for (let i = 0; i < lines.length; i++) {
const currentLine = lines[i];
if (!currentLine) {
continue;
}
drawTextLine(context, currentLine, size, i, fill, stroke);
}
context.globalAlpha = originalGlobalAlpha;
}
function drawTextLine(context, line, size, index, fill, stroke) {
const pos = {
x: -(line.length * size * half$2),
y: size * half$2 + index * size,
};
if (fill) {
context.fillText(line, pos.x, pos.y);
}
if (stroke) {
context.strokeText(line, pos.x, pos.y);
}
}
const firstIndex$2 = 0, minLength$1 = 0;
class TextDrawer {
draw(data) {
drawText(data);
}
async init(container) {
const options = container.actualOptions;
if (validTypes.find(t => isInArray(t, options.particles.shape.type))) {
const shapeOptions = validTypes
.map(t => options.particles.shape.options[t])
.find(t => !!t), promises = [];
executeOnSingleOrMultiple(shapeOptions, shape => {
promises.push(loadFont(shape.font, shape.weight));
});
await Promise.all(promises);
}
}
particleInit(_container, particle) {
if (!particle.shape || !validTypes.includes(particle.shape)) {
return;
}
const character = particle.shapeData;
if (character === undefined) {
return;
}
const textData = character.value;
if (!textData) {
return;
}
particle.textLines = itemFromSingleOrMultiple(textData, particle.randomIndexData)?.split("\n") ?? [];
particle.maxTextLength = particle.textLines.length
? Math.max(...particle.textLines.map(t => t.length))
: (particle.textLines[firstIndex$2]?.length ?? minLength$1);
}
}
async function loadTextShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(validTypes, () => Promise.resolve(new TextDrawer()));
});
}
var TiltDirection;
(function (TiltDirection) {
TiltDirection["clockwise"] = "clockwise";
TiltDirection["counterClockwise"] = "counter-clockwise";
TiltDirection["random"] = "random";
})(TiltDirection || (TiltDirection = {}));
class TiltAnimation {
decay = 0;
enable = false;
speed = 0;
sync = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "decay", data.decay);
loadProperty(this, "sync", data.sync);
}
}
class Tilt extends ValueWithRandom {
animation = new TiltAnimation();
direction = TiltDirection.clockwise;
enable = false;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
this.animation.load(data.animation);
loadProperty(this, "direction", data.direction);
loadProperty(this, "enable", data.enable);
}
}
const maxAngle$2 = 360;
class TiltUpdater {
#container;
constructor(container) {
this.#container = container;
}
getTransformValues(particle) {
const tilt = particle.tilt?.enable && particle.tilt;
return {
b: tilt ? Math.cos(tilt.value) * tilt.cosDirection : undefined,
c: tilt ? Math.sin(tilt.value) * tilt.sinDirection : undefined,
};
}
init(particle) {
const tiltOptions = particle.options.tilt;
if (!tiltOptions) {
return;
}
particle.tilt = {
enable: tiltOptions.enable,
value: degToRad(getRangeValue(tiltOptions.value)),
sinDirection: getRandom() >= half$2 ? identity$3 : -identity$3,
cosDirection: getRandom() >= half$2 ? identity$3 : -identity$3,
min: 0,
max: doublePI,
};
let tiltDirection = tiltOptions.direction;
if (tiltDirection === TiltDirection.random) {
const index = Math.floor(getRandom() * double), minIndex = 0;
tiltDirection = index > minIndex ? TiltDirection.counterClockwise : TiltDirection.clockwise;
}
switch (tiltDirection) {
case TiltDirection.counterClockwise:
case "counterClockwise":
particle.tilt.status = exports.AnimationStatus.decreasing;
break;
case TiltDirection.clockwise:
particle.tilt.status = exports.AnimationStatus.increasing;
break;
}
const tiltAnimation = particle.options.tilt?.animation;
if (tiltAnimation?.enable) {
particle.tilt.decay = identity$3 - getRangeValue(tiltAnimation.decay);
particle.tilt.velocity = (getRangeValue(tiltAnimation.speed) / maxAngle$2) * this.#container.retina.reduceFactor;
if (!tiltAnimation.sync) {
particle.tilt.velocity *= getRandom();
}
}
}
isEnabled(particle) {
const tiltAnimation = particle.options.tilt?.animation;
return !particle.destroyed && !particle.spawning && !!tiltAnimation?.enable;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "tilt", Tilt, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.tilt) {
return;
}
updateAnimation(particle, particle.tilt, false, exports.DestroyType.none, delta);
}
}
async function loadTiltUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("tilt", container => {
return Promise.resolve(new TiltUpdater(container));
});
});
}
class TwinkleLinksValues {
color;
enable = false;
frequency = 0.05;
opacity = 1;
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "frequency", data.frequency);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class TwinkleParticlesValues {
enable = false;
fillColor;
frequency = 0.05;
opacity = 1;
strokeColor;
load(data) {
if (isNull(data)) {
return;
}
if (data.fillColor !== undefined) {
this.fillColor = OptionsColor.create(this.fillColor, data.fillColor);
}
if (data.strokeColor !== undefined) {
this.strokeColor = OptionsColor.create(this.strokeColor, data.strokeColor);
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "frequency", data.frequency);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class Twinkle {
links = new TwinkleLinksValues();
particles = new TwinkleParticlesValues();
load(data) {
if (isNull(data)) {
return;
}
this.links.load(data.links);
this.particles.load(data.particles);
}
}
class TwinkleUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
getColorStyles(particle, _context, _radius, opacity) {
const pOptions = particle.options, container = this.#container, twinkleOptions = pOptions["twinkle"];
if (!twinkleOptions) {
return {};
}
const twinkle = twinkleOptions.particles, twinkling = twinkle.enable && getRandom() < twinkle.frequency, zIndexOptions = particle.options.zIndex, zOffset = 1, zOpacityFactor = (zOffset - particle.zIndexFactor) ** zIndexOptions.opacityRate, twinklingOpacity = twinkling ? getRangeValue(twinkle.opacity) * zOpacityFactor : opacity, twinkleFillRgb = rangeColorToHsl(this.#pluginManager, twinkle.fillColor), twinkleStrokeRgb = rangeColorToHsl(this.#pluginManager, twinkle.strokeColor), getTwinkleFillStyle = () => {
if (!twinkleFillRgb) {
return undefined;
}
return getStyleFromHsl(twinkleFillRgb, container.hdr, twinklingOpacity, container.peakNits, container.hdrMode);
}, getTwinkleStrokeStyle = () => {
if (!twinkleStrokeRgb) {
return undefined;
}
return getStyleFromHsl(twinkleStrokeRgb, container.hdr, twinklingOpacity, container.peakNits, container.hdrMode);
}, twinkleFillStyle = getTwinkleFillStyle(), twinkleStrokeStyle = getTwinkleStrokeStyle(), res = {}, needsTwinkle = twinkling && (!!twinkleFillStyle || !!twinkleStrokeStyle);
res.fill = needsTwinkle ? twinkleFillStyle : undefined;
res.stroke = needsTwinkle ? twinkleStrokeStyle : undefined;
return res;
}
init() {
}
isEnabled(particle) {
const pOptions = particle.options, twinkleOptions = pOptions.twinkle;
if (!twinkleOptions) {
return false;
}
return twinkleOptions.particles.enable;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "twinkle", Twinkle, ...sources);
}
update() {
}
}
async function loadTwinkleUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("twinkle", container => {
return Promise.resolve(new TwinkleUpdater(e.pluginManager, container));
});
});
}
class WobbleSpeed {
angle = 50;
move = 10;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "angle", data.angle);
loadRangeProperty(this, "move", data.move);
}
}
class Wobble {
distance = 5;
enable = false;
speed = new WobbleSpeed();
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "distance", data.distance);
loadProperty(this, "enable", data.enable);
if (data.speed !== undefined) {
if (isNumber(data.speed)) {
this.speed.load({ angle: data.speed });
}
else {
const rangeSpeed = data.speed;
if ("min" in rangeSpeed) {
this.speed.load({ angle: rangeSpeed });
}
else {
this.speed.load(data.speed);
}
}
}
}
}
const defaultDistance$1 = 0, distanceFactor = 60;
function updateWobble(container, particle, delta) {
const { wobble: wobbleOptions } = particle.options, { wobble } = particle;
if (!wobbleOptions?.enable || !wobble) {
return;
}
const reduceFactor = container.retina.reduceFactor, angleSpeed = wobble.angleSpeed * delta.factor * reduceFactor, moveSpeed = wobble.moveSpeed * delta.factor * reduceFactor, distance = (moveSpeed * (particle.retina.wobbleDistance ?? defaultDistance$1)) / (millisecondsToSeconds$1 / distanceFactor), max = doublePI, { position } = particle;
wobble.angle += angleSpeed;
if (wobble.angle > max) {
wobble.angle -= max;
}
position.x += distance * Math.cos(wobble.angle);
position.y += distance * Math.abs(Math.sin(wobble.angle));
}
const maxAngle$1 = 360, moveSpeedFactor$1 = 10, defaultDistance = 0;
class WobbleUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const wobbleOpt = particle.options.wobble;
if (wobbleOpt?.enable) {
particle.wobble = {
angle: getRandom() * doublePI,
angleSpeed: getRangeValue(wobbleOpt.speed.angle) / maxAngle$1,
moveSpeed: getRangeValue(wobbleOpt.speed.move) / moveSpeedFactor$1,
};
}
else {
particle.wobble = {
angle: 0,
angleSpeed: 0,
moveSpeed: 0,
};
}
particle.retina.wobbleDistance =
getRangeValue(wobbleOpt?.distance ?? defaultDistance) * this.#container.retina.pixelRatio;
}
isEnabled(particle) {
return !particle.destroyed && !particle.spawning && !!particle.options.wobble?.enable;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "wobble", Wobble, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
updateWobble(this.#container, particle, delta);
}
}
async function loadWobbleUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("wobble", container => {
return Promise.resolve(new WobbleUpdater(container));
});
});
}
async function loadFull(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const loadEmittersPluginBundle = async (e) => {
await loadEmittersPlugin(e);
await Promise.all([
loadEmittersShapeCircle(e),
loadEmittersShapeSquare(e),
]);
}, loadInteractivityForFull = async (e) => {
await loadSlim(e);
await Promise.all([
loadExternalDragInteraction(e),
loadExternalTrailInteraction(e),
loadAbsorbersPlugin(e),
loadEmittersPluginBundle(e),
]);
};
await Promise.all([
loadInteractivityForFull(e),
loadDestroyUpdater(e),
loadRollUpdater(e),
loadTiltUpdater(e),
loadTwinkleUpdater(e),
loadWobbleUpdater(e),
loadTextShape(e),
]);
});
}
const InterlaceOffsets = [0, 4, 2, 1];
const InterlaceSteps = [8, 8, 4, 2];
class ByteStream {
data;
pos;
constructor(bytes) {
this.pos = 0;
this.data = new Uint8ClampedArray(bytes);
}
getString(count) {
const slice = this.data.slice(this.pos, this.pos + count);
this.pos += slice.length;
return slice.reduce((acc, curr) => acc + String.fromCharCode(curr), "");
}
nextByte() {
return this.data[this.pos++];
}
nextTwoBytes() {
const increment = 2, previous = 1, shift = 8;
this.pos += increment;
return this.data[this.pos - increment] + (this.data[this.pos - previous] << shift);
}
readSubBlocks() {
let blockString = "", size;
const minCount = 0, emptySize = 0;
do {
size = this.data[this.pos++];
for (let count = size; --count >= minCount; blockString += String.fromCharCode(this.data[this.pos++])) {
}
} while (size !== emptySize);
return blockString;
}
readSubBlocksBin() {
let size = this.data[this.pos], len = 0;
const emptySize = 0, increment = 1;
for (let offset = 0; size !== emptySize; offset += size + increment, size = this.data[this.pos + offset]) {
len += size;
}
const blockData = new Uint8Array(len);
size = this.data[this.pos++];
for (let i = 0; size !== emptySize; size = this.data[this.pos++]) {
for (let count = size; --count >= emptySize; blockData[i++] = this.data[this.pos++]) {
}
}
return blockData;
}
skipSubBlocks() {
for (const increment = 1, noData = 0; this.data[this.pos] !== noData; this.pos += this.data[this.pos] + increment) {
}
this.pos++;
}
}
var DisposalMethod;
(function (DisposalMethod) {
DisposalMethod[DisposalMethod["Replace"] = 0] = "Replace";
DisposalMethod[DisposalMethod["Combine"] = 1] = "Combine";
DisposalMethod[DisposalMethod["RestoreBackground"] = 2] = "RestoreBackground";
DisposalMethod[DisposalMethod["RestorePrevious"] = 3] = "RestorePrevious";
DisposalMethod[DisposalMethod["UndefinedA"] = 4] = "UndefinedA";
DisposalMethod[DisposalMethod["UndefinedB"] = 5] = "UndefinedB";
DisposalMethod[DisposalMethod["UndefinedC"] = 6] = "UndefinedC";
DisposalMethod[DisposalMethod["UndefinedD"] = 7] = "UndefinedD";
})(DisposalMethod || (DisposalMethod = {}));
var GIFDataHeaders;
(function (GIFDataHeaders) {
GIFDataHeaders[GIFDataHeaders["Extension"] = 33] = "Extension";
GIFDataHeaders[GIFDataHeaders["ApplicationExtension"] = 255] = "ApplicationExtension";
GIFDataHeaders[GIFDataHeaders["GraphicsControlExtension"] = 249] = "GraphicsControlExtension";
GIFDataHeaders[GIFDataHeaders["PlainTextExtension"] = 1] = "PlainTextExtension";
GIFDataHeaders[GIFDataHeaders["CommentExtension"] = 254] = "CommentExtension";
GIFDataHeaders[GIFDataHeaders["Image"] = 44] = "Image";
GIFDataHeaders[GIFDataHeaders["EndOfFile"] = 59] = "EndOfFile";
})(GIFDataHeaders || (GIFDataHeaders = {}));
const defaultFrame = 0, initialTime = 0, firstIndex$1 = 0, defaultLoopCount = 0;
function parseColorTable(byteStream, count) {
const colors = [];
for (let i = 0; i < count; i++) {
colors.push({
r: byteStream.data[byteStream.pos],
g: byteStream.data[byteStream.pos + 1],
b: byteStream.data[byteStream.pos + 2],
});
byteStream.pos += 3;
}
return colors;
}
function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex) {
switch (byteStream.nextByte()) {
case GIFDataHeaders.GraphicsControlExtension: {
const frame = gif.frames[getFrameIndex(false)];
byteStream.pos++;
const packedByte = byteStream.nextByte();
frame.GCreserved = (packedByte & 0xe0) >>> 5;
frame.disposalMethod = (packedByte & 0x1c) >>> 2;
frame.userInputDelayFlag = (packedByte & 2) === 2;
const transparencyFlag = (packedByte & 1) === 1;
frame.delayTime = byteStream.nextTwoBytes() * 0xa;
const transparencyIndex = byteStream.nextByte();
if (transparencyFlag) {
getTransparencyIndex(transparencyIndex);
}
byteStream.pos++;
break;
}
case GIFDataHeaders.ApplicationExtension: {
byteStream.pos++;
const applicationExtension = {
identifier: byteStream.getString(8),
authenticationCode: byteStream.getString(3),
data: byteStream.readSubBlocksBin(),
};
gif.applicationExtensions.push(applicationExtension);
break;
}
case GIFDataHeaders.CommentExtension: {
gif.comments.push([getFrameIndex(false), byteStream.readSubBlocks()]);
break;
}
case GIFDataHeaders.PlainTextExtension: {
if (gif.globalColorTable.length === 0) {
throw new EvalError("plain text extension without global color table");
}
byteStream.pos++;
gif.frames[getFrameIndex(false)].plainTextData = {
left: byteStream.nextTwoBytes(),
top: byteStream.nextTwoBytes(),
width: byteStream.nextTwoBytes(),
height: byteStream.nextTwoBytes(),
charSize: {
width: byteStream.nextTwoBytes(),
height: byteStream.nextTwoBytes(),
},
foregroundColor: byteStream.nextByte(),
backgroundColor: byteStream.nextByte(),
text: byteStream.readSubBlocks(),
};
break;
}
default:
byteStream.skipSubBlocks();
break;
}
}
function readBits(imageData, pos, len) {
const bytePos = pos >>> 3, bitPos = pos & 7;
return (((imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16)) &
(((1 << len) - 1) << bitPos)) >>>
bitPos);
}
async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings, progressCallback) {
const frame = gif.frames[getFrameIndex(true)];
frame.left = byteStream.nextTwoBytes();
frame.top = byteStream.nextTwoBytes();
frame.width = byteStream.nextTwoBytes();
frame.height = byteStream.nextTwoBytes();
const packedByte = byteStream.nextByte(), localColorTableFlag = (packedByte & 0x80) === 0x80, interlacedFlag = (packedByte & 0x40) === 0x40;
frame.sortFlag = (packedByte & 0x20) === 0x20;
frame.reserved = (packedByte & 0x18) >>> 3;
const localColorCount = 1 << ((packedByte & 7) + 1);
if (localColorTableFlag) {
frame.localColorTable = parseColorTable(byteStream, localColorCount);
}
const getColor = (index) => {
const { r, g, b } = (localColorTableFlag ? frame.localColorTable : gif.globalColorTable)[index];
if (index !== getTransparencyIndex(null)) {
return { r, g, b, a: 255 };
}
return { r, g, b, a: avgAlpha ? Math.trunc((r + g + b) / 3) : 0 };
}, image = (() => {
try {
return new ImageData(frame.width, frame.height, canvasSettings);
}
catch (error) {
if (error instanceof DOMException && error.name === "IndexSizeError") {
return null;
}
throw error;
}
})();
if (image == null) {
throw new EvalError("GIF frame size is to large");
}
const minCodeSize = byteStream.nextByte(), imageData = byteStream.readSubBlocksBin(), clearCode = 1 << minCodeSize;
if (interlacedFlag) {
for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) {
if (InterlaceOffsets[pass] < frame.height) {
let pixelPos = 0, lineIndex = 0, exit = false;
while (!exit) {
const last = code;
code = readBits(imageData, pos, size);
pos += size + 1;
if (code === clearCode) {
size = minCodeSize + 1;
dic.length = clearCode + 2;
for (let i = 0; i < dic.length; i++) {
dic[i] = i < clearCode ? [i] : [];
}
}
else {
if (code >= dic.length) {
dic.push(dic[last].concat(dic[last][0]));
}
else if (last !== clearCode) {
dic.push(dic[last].concat(dic[code][0]));
}
for (const item of dic[code]) {
const { r, g, b, a } = getColor(item);
image.data.set([r, g, b, a], InterlaceOffsets[pass] * frame.width +
InterlaceSteps[pass] * lineIndex +
(pixelPos % (frame.width * 4)));
pixelPos += 4;
}
if (dic.length === 1 << size && size < 0xc) {
size++;
}
}
if (pixelPos === frame.width * 4 * (lineIndex + 1)) {
lineIndex++;
if (InterlaceOffsets[pass] + InterlaceSteps[pass] * lineIndex >= frame.height) {
exit = true;
}
}
}
}
}
frame.image = image;
frame.bitmap = await createImageBitmap(image);
}
else {
let code = 0, size = minCodeSize + 1, pos = 0, pixelPos = -4;
const dic = [[0]];
for (;;) {
const last = code;
code = readBits(imageData, pos, size);
pos += size;
if (code === clearCode) {
size = minCodeSize + 1;
dic.length = clearCode + 2;
for (let i = 0; i < dic.length; i++) {
dic[i] = i < clearCode ? [i] : [];
}
}
else {
if (code === clearCode + 1) {
break;
}
if (code >= dic.length) {
dic.push(dic[last].concat(dic[last][0]));
}
else if (last !== clearCode) {
dic.push(dic[last].concat(dic[code][0]));
}
for (const item of dic[code]) {
const { r, g, b, a } = getColor(item);
pixelPos += 4;
image.data.set([r, g, b, a], pixelPos);
}
if (dic.length >= 1 << size && size < 0xc) {
size++;
}
}
}
frame.image = image;
frame.bitmap = await createImageBitmap(image);
}
}
async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings, progressCallback) {
switch (byteStream.nextByte()) {
case GIFDataHeaders.EndOfFile:
return true;
case GIFDataHeaders.Image:
await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings);
break;
case GIFDataHeaders.Extension:
parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex);
break;
default:
throw new EvalError("undefined block found");
}
return false;
}
function getGIFLoopAmount(gif) {
for (const extension of gif.applicationExtensions) {
if (extension.identifier + extension.authenticationCode !== "NETSCAPE2.0") {
continue;
}
return extension.data[1] + (extension.data[2] << 8);
}
return Number.NaN;
}
async function decodeGIF(gifURL, canvasSettings, progressCallback, avgAlpha) {
avgAlpha ??= false;
const res = await fetch(gifURL);
if (!res.ok && res.status === 404) {
throw new EvalError("file not found");
}
const buffer = await res.arrayBuffer(), gif = {
width: 0,
height: 0,
totalTime: 0,
colorRes: 0,
pixelAspectRatio: 0,
frames: [],
sortFlag: false,
globalColorTable: [],
backgroundImage: new ImageData(1, 1, canvasSettings),
comments: [],
applicationExtensions: [],
}, byteStream = new ByteStream(new Uint8ClampedArray(buffer));
if (byteStream.getString(6) !== "GIF89a") {
throw new Error("not a supported GIF file");
}
gif.width = byteStream.nextTwoBytes();
gif.height = byteStream.nextTwoBytes();
const packedByte = byteStream.nextByte(), globalColorTableFlag = (packedByte & 0x80) === 0x80;
gif.colorRes = (packedByte & 0x70) >>> 4;
gif.sortFlag = (packedByte & 8) === 8;
const globalColorCount = 1 << ((packedByte & 7) + 1), backgroundColorIndex = byteStream.nextByte();
gif.pixelAspectRatio = byteStream.nextByte();
if (gif.pixelAspectRatio !== 0) {
gif.pixelAspectRatio = (gif.pixelAspectRatio + 0xf) / 0x40;
}
if (globalColorTableFlag) {
gif.globalColorTable = parseColorTable(byteStream, globalColorCount);
}
const backgroundImage = (() => {
try {
return new ImageData(gif.width, gif.height, canvasSettings);
}
catch (error) {
if (error instanceof DOMException && error.name === "IndexSizeError") {
return null;
}
throw error;
}
})();
if (backgroundImage == null) {
throw new Error("GIF frame size is to large");
}
const { r, g, b } = gif.globalColorTable[backgroundColorIndex];
backgroundImage.data.set(globalColorTableFlag ? [r, g, b, 255] : [0, 0, 0, 0]);
for (let i = 4; i < backgroundImage.data.length; i *= 2) {
backgroundImage.data.copyWithin(i, 0, i);
}
gif.backgroundImage = backgroundImage;
let frameIndex = -1, incrementFrameIndex = true, transparencyIndex = -1;
const getframeIndex = (increment) => {
if (increment) {
incrementFrameIndex = true;
}
return frameIndex;
}, getTransparencyIndex = (newValue) => {
if (newValue != null) {
transparencyIndex = newValue;
}
return transparencyIndex;
};
try {
do {
if (incrementFrameIndex) {
gif.frames.push({
left: 0,
top: 0,
width: 0,
height: 0,
disposalMethod: DisposalMethod.Replace,
image: new ImageData(1, 1, canvasSettings),
plainTextData: null,
userInputDelayFlag: false,
delayTime: 0,
sortFlag: false,
localColorTable: [],
reserved: 0,
GCreserved: 0,
});
frameIndex++;
transparencyIndex = -1;
incrementFrameIndex = false;
}
} while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, canvasSettings, progressCallback)));
gif.frames.length--;
for (const frame of gif.frames) {
if (frame.userInputDelayFlag && frame.delayTime === 0) {
gif.totalTime = Infinity;
break;
}
gif.totalTime += frame.delayTime;
}
return gif;
}
catch (error) {
if (error instanceof EvalError) {
throw new Error(`error while parsing frame ${frameIndex.toString()} "${error.message}"`, { cause: error });
}
throw error;
}
}
function drawGif(data, canvasSettings) {
const { context, radius, particle, delta } = data, gifData = particle.gifData;
if (!gifData) {
return;
}
const offscreenCanvas = new OffscreenCanvas(gifData.width, gifData.height), offscreenContext = offscreenCanvas.getContext("2d", canvasSettings);
if (!offscreenContext) {
throw new Error("could not create offscreen canvas context");
}
offscreenContext.imageSmoothingQuality = "low";
offscreenContext.imageSmoothingEnabled = false;
offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
let frameIndex = particle.gifFrame ?? defaultFrame;
const pos = { x: -gifData.width * half$2, y: -gifData.height * half$2 }, frame = gifData.frames[frameIndex];
particle.gifTime ??= initialTime;
if (!frame.bitmap) {
return;
}
context.scale(radius / gifData.width, radius / gifData.height);
switch (frame.disposalMethod) {
case DisposalMethod.UndefinedA:
case DisposalMethod.UndefinedB:
case DisposalMethod.UndefinedC:
case DisposalMethod.UndefinedD:
case DisposalMethod.Replace:
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
context.drawImage(offscreenCanvas, pos.x, pos.y);
offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
break;
case DisposalMethod.Combine:
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
context.drawImage(offscreenCanvas, pos.x, pos.y);
break;
case DisposalMethod.RestoreBackground:
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
context.drawImage(offscreenCanvas, pos.x, pos.y);
offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
if (!gifData.globalColorTable.length) {
offscreenContext.putImageData(gifData.frames[firstIndex$1].image, pos.x + frame.left, pos.y + frame.top);
}
else {
offscreenContext.putImageData(gifData.backgroundImage, pos.x, pos.y);
}
break;
case DisposalMethod.RestorePrevious:
{
const previousImageData = offscreenContext.getImageData(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
context.drawImage(offscreenCanvas, pos.x, pos.y);
offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
offscreenContext.putImageData(previousImageData, originPoint.x, originPoint.y);
}
break;
}
particle.gifTime += delta.value;
if (particle.gifTime > frame.delayTime) {
particle.gifTime -= frame.delayTime;
if (++frameIndex >= gifData.frames.length) {
if (particle.gifLoopCount == null || --particle.gifLoopCount <= defaultLoopCount) {
return;
}
frameIndex = firstIndex$1;
offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
}
particle.gifFrame = frameIndex;
}
context.scale(gifData.width / radius, gifData.height / radius);
}
class GifDrawer {
#container;
#gifCache;
constructor(container) {
this.#container = container;
this.#gifCache = new Map();
}
destroy() {
this.#gifCache.clear();
}
draw(data) {
const { context, opacity } = data;
if (!data.particle.gifData)
return;
context.globalAlpha = opacity;
drawGif(data, this.#container.canvas.render.settings);
context.globalAlpha = 1;
}
loadShape(particle) {
const shapeData = particle.shapeData;
if (!shapeData?.src)
return;
if (this.#gifCache.has(shapeData.src))
return;
void decodeGIF(shapeData.src, { colorSpace: "srgb" }).then(gifData => {
this.#gifCache.set(shapeData.src, gifData);
});
}
particleInit(container, particle) {
const shapeData = particle.shapeData;
if (!shapeData?.src)
return;
const gifData = this.#gifCache.get(shapeData.src);
if (!gifData) {
setTimeout(() => {
this.particleInit(container, particle);
});
return;
}
particle.gifData = gifData;
particle.gifFrame = 0;
particle.gifTime = 0;
particle.gifLoopCount = getGIFLoopAmount(gifData);
if (particle.gifLoopCount === 0) {
particle.gifLoopCount = Number.POSITIVE_INFINITY;
}
if (shapeData.loopCount !== undefined) {
particle.gifLoopCount = shapeData.loopCount;
}
}
}
async function loadGifShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["gif", "gifs"], container => Promise.resolve(new GifDrawer(container)));
});
}
class GradientColorOpacityAnimation {
count = 0;
decay = 0;
delay = 0;
enable = false;
speed = 0;
startValue = exports.StartValueType.random;
sync = false;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "count", data.count);
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadProperty(this, "sync", data.sync);
loadProperty(this, "startValue", data.startValue);
loadRangeProperty(this, "decay", data.decay);
loadRangeProperty(this, "delay", data.delay);
}
}
class GradientColorOpacity {
animation = new GradientColorOpacityAnimation();
value = 0;
load(data) {
if (isNull(data)) {
return;
}
this.animation.load(data.animation);
loadRangeProperty(this, "value", data.value);
}
}
class AnimatableGradientColor {
opacity;
stop = 0;
value = new AnimatableColor();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "stop", data.stop);
this.value = AnimatableColor.create(this.value, data.value);
if (data.opacity !== undefined) {
this.opacity = new GradientColorOpacity();
if (isNumber(data.opacity)) {
this.opacity.value = data.opacity;
}
else {
this.opacity.load(data.opacity);
}
}
}
}
class GradientAngleAnimation {
count = 0;
decay = 0;
delay = 0;
enable = false;
speed = 0;
sync = false;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "count", data.count);
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "decay", data.decay);
loadRangeProperty(this, "delay", data.delay);
loadProperty(this, "sync", data.sync);
}
}
class GradientAngle {
animation = new GradientAngleAnimation();
direction = exports.RotateDirection.clockwise;
value = 0;
load(data) {
if (isNull(data)) {
return;
}
this.animation.load(data.animation);
loadRangeProperty(this, "value", data.value);
loadProperty(this, "direction", data.direction);
}
}
class AnimatableGradient {
angle = new GradientAngle();
colors = [];
type = exports.GradientType.random;
load(data) {
if (isNull(data)) {
return;
}
this.angle.load(data.angle);
if (data.colors !== undefined) {
this.colors = data.colors.map(s => {
const tmp = new AnimatableGradientColor();
tmp.load(s);
return tmp;
});
}
loadProperty(this, "type", data.type);
}
}
function updateGradient(particle, delta, hdr) {
const { gradient } = particle;
if (!gradient) {
return;
}
updateAnimation(particle, gradient.angle, false, exports.DestroyType.none, delta);
for (const color of gradient.colors) {
updateColor(color.value, delta);
if (color.opacity) {
updateAnimation(particle, color.opacity, true, exports.DestroyType.none, delta);
}
}
}
class GradientUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
getColorStyles(particle, context, radius, opacity) {
const gradient = particle.gradient;
if (!gradient) {
return {};
}
const container = this.#container, gradientAngle = gradient.angle.value, origin = { x: 0, y: 0 }, minRadius = 0, fillGradient = gradient.type === exports.GradientType.radial
? context.createRadialGradient(origin.x, origin.y, minRadius, origin.x, origin.y, radius)
: context.createLinearGradient(Math.cos(gradientAngle) * -radius, Math.sin(gradientAngle) * -radius, Math.cos(gradientAngle) * radius, Math.sin(gradientAngle) * radius);
for (const { stop, value, opacity: cOpacity } of gradient.colors) {
const hsl = {
h: value.h.value,
s: value.s.value,
l: value.l.value,
};
fillGradient.addColorStop(stop, getStyleFromHsl(hsl, container.hdr, cOpacity?.value ?? opacity, container.peakNits, container.hdrMode));
}
return { fill: fillGradient };
}
init(particle) {
const gradient = itemFromSingleOrMultiple(particle.options.gradient);
if (!gradient) {
return;
}
const container = this.#container, { angle } = gradient, speedFactor = 360, delayOffset = 1;
particle.gradient = {
angle: {
value: getRangeValue(angle.value),
enable: angle.animation.enable,
velocity: (getRangeValue(angle.animation.speed) / speedFactor) * container.retina.reduceFactor,
decay: delayOffset - getRangeValue(angle.animation.decay),
delayTime: getRangeValue(angle.animation.delay) * millisecondsToSeconds$1,
max: doublePI,
min: 0,
time: 0,
},
type: gradient.type,
colors: [],
};
let rotateDirection = gradient.angle.direction;
if (rotateDirection === exports.RotateDirection.random) {
rotateDirection = getRandom() > half$2 ? exports.RotateDirection.counterClockwise : exports.RotateDirection.clockwise;
}
switch (rotateDirection) {
case exports.RotateDirection.counterClockwise:
case "counterClockwise":
particle.gradient.angle.status = exports.AnimationStatus.decreasing;
break;
case exports.RotateDirection.clockwise:
particle.gradient.angle.status = exports.AnimationStatus.increasing;
break;
}
const reduceDuplicates = particle.options.reduceDuplicates;
for (const grColor of gradient.colors) {
const grHslColor = rangeColorToHsl(this.#pluginManager, grColor.value, particle.id, reduceDuplicates);
if (!grHslColor) {
continue;
}
const grHslAnimation = getHslAnimationFromHsl(grHslColor, grColor.value.animation, container.retina.reduceFactor), addColor = {
stop: grColor.stop,
value: grHslAnimation,
opacity: grColor.opacity
? {
enable: grColor.opacity.animation.enable,
max: getRangeMax(grColor.opacity.value),
min: getRangeMin(grColor.opacity.value),
status: exports.AnimationStatus.increasing,
value: getRangeValue(grColor.opacity.value),
velocity: (getRangeValue(grColor.opacity.animation.speed) / percentDenominator) * container.retina.reduceFactor,
decay: delayOffset - getRangeValue(grColor.opacity.animation.decay),
delayTime: getRangeValue(grColor.opacity.animation.delay) * millisecondsToSeconds$1,
time: 0,
}
: undefined,
}, { opacity: addOpacity } = addColor;
if (grColor.opacity && addOpacity) {
const opacityRange = grColor.opacity.value;
addOpacity.min = getRangeMin(opacityRange);
addOpacity.max = getRangeMax(opacityRange);
const opacityAnimation = grColor.opacity.animation;
switch (opacityAnimation.startValue) {
case exports.StartValueType.min:
addOpacity.value = addOpacity.min;
addOpacity.status = exports.AnimationStatus.increasing;
break;
case exports.StartValueType.max:
addOpacity.value = addOpacity.max;
addOpacity.status = exports.AnimationStatus.decreasing;
break;
case exports.StartValueType.random:
default:
addOpacity.value = randomInRangeValue(addOpacity);
addOpacity.status = getRandom() >= half$2 ? exports.AnimationStatus.increasing : exports.AnimationStatus.decreasing;
break;
}
}
particle.gradient.colors.push(addColor);
}
}
isEnabled(particle) {
return (!particle.destroyed &&
!particle.spawning &&
(!!particle.gradient?.angle.enable ||
(particle.gradient?.colors.some(c => c.value.h.enable || c.value.s.enable || c.value.l.enable) ?? false)));
}
loadOptions(options, ...sources) {
for (const source of sources) {
if (!source?.gradient) {
continue;
}
const gradientToLoad = source.gradient;
options.gradient = executeOnSingleOrMultiple(gradientToLoad, gradient => {
const tmp = new AnimatableGradient();
tmp.load(gradient);
return tmp;
});
}
}
update(particle, delta) {
updateGradient(particle, delta, this.#container.hdr);
}
}
async function loadGradientUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("gradient", container => {
return Promise.resolve(new GradientUpdater(e.pluginManager, container));
});
});
}
const dirs = [
Vector.create(identity$3, originPoint.y),
Vector.create(originPoint.x, identity$3),
Vector.create(-identity$3, originPoint.y),
Vector.create(originPoint.x, -identity$3),
], opposite = [2, 3, 0, 1], minLength = 0;
class GridPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = {
cellSize: 40,
graph: undefined,
autoMaze: false,
};
}
generate(p) {
const size = this.options.cellSize;
p.grid ??= {
direction: Math.trunc(getRandom() * dirs.length),
speed: p.velocity.length,
cellPosition: {
x: Math.floor(p.position.x / size),
y: Math.floor(p.position.y / size),
},
};
const grid = p.grid, c = {
x: Math.floor(p.position.x / size),
y: Math.floor(p.position.y / size),
};
if (c.x !== grid.cellPosition.x || c.y !== grid.cellPosition.y) {
grid.cellPosition.x = c.x;
grid.cellPosition.y = c.y;
const key = `${c.x},${c.y}`, allowed = this.options.graph?.[key];
if (allowed && allowed.length > minLength) {
grid.direction = allowed[Math.trunc(getRandom() * allowed.length)];
}
else if (!this.options.graph) {
const turn = Math.trunc(getRandom() * 3) - 1;
grid.direction = (grid.direction + turn + 4) % dirs.length;
}
}
p.velocity.x = 0;
p.velocity.y = 0;
const d = dirs[grid.direction];
this.#res.x = d.x * grid.speed;
this.#res.y = d.y * grid.speed;
return this.#res;
}
init() {
const source = this.#container.actualOptions.particles.move.path.options;
this.options.cellSize = source["cellSize"] ?? this.options.cellSize;
this.options.autoMaze = source["autoMaze"] ?? this.options.autoMaze;
this.options.graph = source["graph"];
if (this.options.autoMaze) {
const canvas = this.#container.canvas.size, cellSize = this.options.cellSize, size = {
width: Math.ceil(canvas.width / cellSize),
height: Math.ceil(canvas.height / cellSize),
};
this.options.graph = this.#generateMazeGraph(size);
}
}
reset(p) {
delete p.grid;
}
update() {
}
#generateMazeGraph({ height, width }) {
const graph = {}, visited = [];
for (let y = 0; y < height; y++) {
visited[y] = [];
}
const stack = [{ x: originPoint.x, y: originPoint.y }];
while (stack.length > 0) {
const { x, y } = stack[stack.length - 1];
if (!visited[y][x]) {
visited[y][x] = true;
}
const dirsOrder = [0, 1, 2, 3].sort(() => getRandom() - 0.5);
let moved = false;
for (const d of dirsOrder) {
const n = {
x: x + (d === 0 ? 1 : d === 2 ? -1 : 0),
y: y + (d === 1 ? 1 : d === 3 ? -1 : 0),
};
if (n.x < 0 || n.y < 0 || n.x >= width || n.y >= height) {
continue;
}
if (!visited[n.y][n.x]) {
const key = `${x},${y}`, nKey = `${n.x},${n.y}`;
graph[key] ??= [];
graph[nKey] ??= [];
graph[key].push(d);
graph[nKey].push(opposite[d]);
stack.push(n);
moved = true;
break;
}
}
if (!moved) {
stack.pop();
}
}
return graph;
}
}
const gridPathName = "gridPathGenerator";
async function loadGridPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(gridPathName, container => {
return Promise.resolve(new GridPathGenerator(container));
});
});
}
function drawHeart(data) {
const { context, radius } = data, diameter = radius * double, halfRadius = radius * half$2, radiusAndHalf = radius + halfRadius, x = -radius, y = -radius;
context.moveTo(x, y + halfRadius);
context.quadraticCurveTo(x, y, x + halfRadius, y);
context.quadraticCurveTo(x + radius, y, x + radius, y + halfRadius);
context.quadraticCurveTo(x + radius, y, x + radiusAndHalf, y);
context.quadraticCurveTo(x + diameter, y, x + diameter, y + halfRadius);
context.quadraticCurveTo(x + diameter, y + radius, x + radiusAndHalf, y + radiusAndHalf);
context.lineTo(x + radius, y + diameter);
context.lineTo(x + halfRadius, y + radiusAndHalf);
context.quadraticCurveTo(x, y + radius, x, y + halfRadius);
}
class HeartDrawer {
draw(data) {
drawHeart(data);
}
}
async function loadHeartShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["heart"], () => Promise.resolve(new HeartDrawer()));
});
}
function hsvToRgb(hsv) {
const result = { b: 0, g: 0, r: 0 }, phase = 60, hsvPercent = {
h: hsv.h / phase,
s: hsv.s / percentDenominator,
v: hsv.v / percentDenominator,
}, offset = 1, hPercentFactor = 2, c = hsvPercent.v * hsvPercent.s, x = c * (offset - Math.abs((hsvPercent.h % hPercentFactor) - offset));
let tempRgb;
const cxzRange = { min: 0, max: 1 }, xczRange = { min: 1, max: 2 }, zcxRange = { min: 2, max: 3 }, zxcRange = { min: 3, max: 4 }, xzcRange = { min: 4, max: 5 }, czxRange = { min: 5, max: 6 };
if (hsvPercent.h >= cxzRange.min && hsvPercent.h <= cxzRange.max) {
tempRgb = {
r: c,
g: x,
b: 0,
};
}
else if (hsvPercent.h > xczRange.min && hsvPercent.h <= xczRange.max) {
tempRgb = {
r: x,
g: c,
b: 0,
};
}
else if (hsvPercent.h > zcxRange.min && hsvPercent.h <= zcxRange.max) {
tempRgb = {
r: 0,
g: c,
b: x,
};
}
else if (hsvPercent.h > zxcRange.min && hsvPercent.h <= zxcRange.max) {
tempRgb = {
r: 0,
g: x,
b: c,
};
}
else if (hsvPercent.h > xzcRange.min && hsvPercent.h <= xzcRange.max) {
tempRgb = {
r: x,
g: 0,
b: c,
};
}
else if (hsvPercent.h > czxRange.min && hsvPercent.h <= czxRange.max) {
tempRgb = {
r: c,
g: 0,
b: x,
};
}
if (tempRgb) {
const m = hsvPercent.v - c;
result.r = Math.floor((tempRgb.r + m) * rgbMax);
result.g = Math.floor((tempRgb.g + m) * rgbMax);
result.b = Math.floor((tempRgb.b + m) * rgbMax);
}
return result;
}
function hsvaToRgba(hsva) {
return {
a: hsva.a,
...hsvToRgb(hsva),
};
}
const hsvRegex = /hsva?\(\s*(\d+)°\s*[\s,]\s*(\d+)%\s*[\s,]\s*(\d+)%\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i;
class HsvColorManager {
accepts(input) {
return input.startsWith("hsv");
}
handleColor(color) {
const colorValue = color.value, hsvColor = colorValue.hsv ?? color.value;
if (!("h" in hsvColor) || !("s" in hsvColor) || !("v" in hsvColor)) {
return;
}
return hsvToRgb(hsvColor);
}
handleRangeColor(color) {
const colorValue = color.value, hsvColor = colorValue.hsv ?? color.value;
if (!("h" in hsvColor) || !("s" in hsvColor) || !("v" in hsvColor)) {
return;
}
return hsvToRgb({
h: getRangeValue(hsvColor.h),
s: getRangeValue(hsvColor.s),
v: getRangeValue(hsvColor.v),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = hsvRegex.exec(input), fullLength = 4, indexes = {
h: 1,
s: 2,
v: 3,
a: 5,
}, defaultAlpha = 1, radix = 10;
return result
? hsvaToRgba({
a: result.length > fullLength ? parseAlpha(result[indexes.a]) : defaultAlpha,
h: parseInt(result[indexes.h] ?? "0", radix),
s: parseInt(result[indexes.s] ?? "0", radix),
v: parseInt(result[indexes.v] ?? "0", radix),
})
: undefined;
}
}
async function loadHsvColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hsv", new HsvColorManager());
});
}
var HwbIndexes;
(function (HwbIndexes) {
HwbIndexes[HwbIndexes["h"] = 1] = "h";
HwbIndexes[HwbIndexes["w"] = 2] = "w";
HwbIndexes[HwbIndexes["b"] = 3] = "b";
HwbIndexes[HwbIndexes["a"] = 5] = "a";
})(HwbIndexes || (HwbIndexes = {}));
const hwbRegex = /hwba?\(\s*(\d{1,3}(?:\.\d+)?(?:deg|rad|grad|turn)?)\s*[\s,]\s*(\d{1,3}(?:\.\d+)?)%\s*[\s,]\s*(\d{1,3}(?:\.\d+)?)%\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i, maxDegree = 360, maxPercentage = 100, maxHwbValue = 255, hueSectors = 6, redOffset = 5, greenOffset = 3, blueOffset = 1, clampingMin = 0, clampingMax = 1, clampingReference = 4, unitValue = 1, defaultFallbackValue = 0;
function hwbToRgb(hwb) {
const normalizedHue = hwb.h / maxDegree, whiteness = hwb.w / maxPercentage, blackness = hwb.b / maxPercentage, totalWhitenessBlackness = whiteness + blackness;
let red, green, blue;
if (totalWhitenessBlackness >= unitValue) {
const greyShade = whiteness / totalWhitenessBlackness;
red = greyShade;
green = greyShade;
blue = greyShade;
}
else {
const calculateChannel = (channelOffset) => {
const sectorPosition = (channelOffset + normalizedHue * hueSectors) % hueSectors, availableChroma = unitValue - whiteness - blackness, intensityFactor = Math.max(clampingMin, Math.min(sectorPosition, clampingReference - sectorPosition, clampingMax));
return unitValue - blackness - availableChroma * intensityFactor;
};
red = calculateChannel(redOffset);
green = calculateChannel(greenOffset);
blue = calculateChannel(blueOffset);
}
return {
r: Math.round(red * maxHwbValue),
g: Math.round(green * maxHwbValue),
b: Math.round(blue * maxHwbValue),
};
}
function hwbaToRgba(hwba) {
return {
a: hwba.a,
...hwbToRgb(hwba),
};
}
class HwbColorManager {
accepts(input) {
return input.startsWith("hwb");
}
handleColor(color) {
const colorValue = color.value, hwbColor = colorValue.hwb ?? color.value;
if (!("h" in hwbColor) || !("w" in hwbColor) || !("b" in hwbColor)) {
return;
}
return hwbToRgb(hwbColor);
}
handleRangeColor(color) {
const colorValue = color.value, hwbColor = colorValue.hwb ?? color.value;
if (!("h" in hwbColor) || !("w" in hwbColor) || !("b" in hwbColor)) {
return;
}
return hwbToRgb({
h: getRangeValue(hwbColor.h),
w: getRangeValue(hwbColor.w),
b: getRangeValue(hwbColor.b),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = hwbRegex.exec(input), minLength = 4, defaultAlpha = 1;
if (!result) {
return;
}
const h = result[HwbIndexes.h], w = result[HwbIndexes.w], b = result[HwbIndexes.b];
return hwbaToRgba({
a: result.length > minLength ? parseAlpha(result[HwbIndexes.a]) : defaultAlpha,
h: h !== undefined ? parseFloat(h) : defaultFallbackValue,
w: w !== undefined ? parseFloat(w) : defaultFallbackValue,
b: b !== undefined ? parseFloat(b) : defaultFallbackValue,
});
}
}
async function loadHwbColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hwb", new HwbColorManager());
});
}
class InfectionStage {
color = new OptionsColor();
duration;
infectedStage;
radius = 0;
rate = 1;
constructor() {
this.color.value = "#ff0000";
}
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
this.duration = data.duration;
this.infectedStage = data.infectedStage;
loadProperty(this, "radius", data.radius);
loadProperty(this, "rate", data.rate);
}
}
class Infection {
cure;
delay;
enable;
infections;
stages;
constructor() {
this.cure = false;
this.delay = 0;
this.enable = false;
this.infections = 0;
this.stages = [];
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "cure", data.cure);
loadProperty(this, "delay", data.delay);
loadProperty(this, "enable", data.enable);
loadProperty(this, "infections", data.infections);
if (data.stages === undefined) {
return;
}
this.stages = data.stages.map(t => {
const s = new InfectionStage();
s.load(t);
return s;
});
}
}
class InfectionPlugin {
id = "infection";
async getPlugin(container) {
const { InfectionPluginInstance } = await Promise.resolve().then(function () { return InfectionPluginInstance$1; });
return new InfectionPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let infectionOptions = options.infection;
if (infectionOptions?.load === undefined) {
options.infection = infectionOptions = new Infection();
}
infectionOptions.load(source?.infection);
}
needsPlugin(options) {
return options?.infection?.enable ?? false;
}
}
const minStagesCount = 1;
class ParticlesInfecter extends ParticlesInteractorBase {
#maxDistance;
constructor(container) {
super(container);
this.#maxDistance = 0;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
}
interact(p1, _interactivityData, delta) {
const infecter = this.container.infecter;
if (!infecter) {
return;
}
infecter.updateInfection(p1, delta.value);
if (p1.infection?.stage === undefined) {
return;
}
const container = this.container, options = container.actualOptions, infectionOptions = options.infection;
if (!infectionOptions?.enable || infectionOptions.stages.length < minStagesCount) {
return;
}
const infectionStage1 = infectionOptions.stages[p1.infection.stage];
if (!infectionStage1) {
return;
}
this.#maxDistance = Math.max(this.#maxDistance, infectionStage1.radius);
const pxRatio = container.retina.pixelRatio, radius = p1.getRadius() * double + infectionStage1.radius * pxRatio, pos = p1.getPosition(), infectedStage1 = infectionStage1.infectedStage ?? p1.infection.stage, query = container.particles.grid.queryCircle(pos, radius), infections = infectionStage1.rate, neighbors = query.length;
for (const p2 of query) {
const infP2 = p2;
if (infP2 === p1 ||
infP2.destroyed ||
infP2.spawning ||
!(infP2.infection?.stage === undefined || infP2.infection.stage !== p1.infection.stage) ||
getRandom() >= infections / neighbors) {
continue;
}
if (infP2.infection?.stage === undefined) {
infecter.startInfection(infP2, infectedStage1);
}
else if (infP2.infection.stage < p1.infection.stage) {
infecter.updateInfectionStage(infP2, infectedStage1);
}
else if (infP2.infection.stage > p1.infection.stage) {
const infectionStage2 = infectionOptions.stages[infP2.infection.stage];
if (!infectionStage2) {
continue;
}
const infectedStage2 = infectionStage2.infectedStage ?? infP2.infection.stage;
infecter.updateInfectionStage(p1, infectedStage2);
}
}
}
isEnabled() {
return this.container.actualOptions.infection?.enable ?? false;
}
reset() {
}
}
async function loadInfectionPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addPlugin(new InfectionPlugin());
e.pluginManager.addInteractor?.("particlesInfection", (container) => {
return Promise.resolve(new ParticlesInfecter(container));
});
});
}
const loopSizeFactor = 0.55;
function drawInfinity(data) {
const { context, radius } = data, loopControl = radius * loopSizeFactor;
context.moveTo(originPoint.x, originPoint.y);
context.bezierCurveTo(loopControl, -radius, loopControl, radius, originPoint.x, originPoint.y);
context.moveTo(originPoint.x, originPoint.y);
context.bezierCurveTo(-loopControl, -radius, originPoint.x - loopControl, radius, originPoint.x, originPoint.y);
}
class InfinityDrawer {
draw(data) {
drawInfinity(data);
}
}
async function loadInfinityShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["infinity"], () => Promise.resolve(new InfinityDrawer()));
});
}
const RGB$1 = {
MAX: 255,
}, WHITE_POINT_D65$1 = {
X: 0.95047,
Y: 1.0,
Z: 1.08883,
}, CIE$1 = {
LAB_EPSILON_NUMERATOR: 216,
LAB_EPSILON_DENOMINATOR: 24389,
LAB_KAPPA_NUMERATOR: 24389,
LAB_KAPPA_DENOMINATOR: 27,
LAB_L_OFFSET: 16,
LAB_L_SCALE: 116,
LAB_A_SCALE: 500,
LAB_B_SCALE: 200,
}, LAB$1 = {
EPSILON: CIE$1.LAB_EPSILON_NUMERATOR / CIE$1.LAB_EPSILON_DENOMINATOR,
KAPPA: CIE$1.LAB_KAPPA_NUMERATOR / CIE$1.LAB_KAPPA_DENOMINATOR,
L_OFFSET: CIE$1.LAB_L_OFFSET,
L_SCALE: CIE$1.LAB_L_SCALE,
A_SCALE: CIE$1.LAB_A_SCALE,
B_SCALE: CIE$1.LAB_B_SCALE,
}, XYZ_TO_LINEAR_RGB$1 = {
r: { x: 3.2404542, y: -1.5371385, z: -0.4985314 },
g: { x: -0.969266, y: 1.8760108, z: 0.041556 },
b: { x: 0.0556434, y: -0.2040259, z: 1.0572252 },
}, SRGB$3 = {
GAMMA: 2.4,
LINEAR_THRESHOLD: 0.0031308,
LINEAR_SCALE: 12.92,
SCALE: 1.055,
OFFSET: 0.055,
}, minSrgbValue$3 = 0, maxSrgbValue$3 = 1;
function labToRgb(lab) {
const L = lab.l, a = lab.aAxis, b = lab.bAxis, fy = (L + LAB$1.L_OFFSET) / LAB$1.L_SCALE, fx = fy + a / LAB$1.A_SCALE, fz = fy - b / LAB$1.B_SCALE, cubic = 3, fx3 = fx ** cubic, fz3 = fz ** cubic, xr = fx3 > LAB$1.EPSILON ? fx3 : (LAB$1.L_SCALE * fx - LAB$1.L_OFFSET) / LAB$1.KAPPA, yr = L > LAB$1.KAPPA * LAB$1.EPSILON ? fy ** cubic : L / LAB$1.KAPPA, zr = fz3 > LAB$1.EPSILON ? fz3 : (LAB$1.L_SCALE * fz - LAB$1.L_OFFSET) / LAB$1.KAPPA, X = xr * WHITE_POINT_D65$1.X, Y = yr * WHITE_POINT_D65$1.Y, Z = zr * WHITE_POINT_D65$1.Z, rLinear = XYZ_TO_LINEAR_RGB$1.r.x * X + XYZ_TO_LINEAR_RGB$1.r.y * Y + XYZ_TO_LINEAR_RGB$1.r.z * Z, gLinear = XYZ_TO_LINEAR_RGB$1.g.x * X + XYZ_TO_LINEAR_RGB$1.g.y * Y + XYZ_TO_LINEAR_RGB$1.g.z * Z, bLinear = XYZ_TO_LINEAR_RGB$1.b.x * X + XYZ_TO_LINEAR_RGB$1.b.y * Y + XYZ_TO_LINEAR_RGB$1.b.z * Z, toSrgb = (x) => x <= SRGB$3.LINEAR_THRESHOLD
? SRGB$3.LINEAR_SCALE * x
: SRGB$3.SCALE * Math.pow(x, inverseFactorNumerator / SRGB$3.GAMMA) - SRGB$3.OFFSET, toSrgbFixed = num => Math.round(clamp(toSrgb(num), minSrgbValue$3, maxSrgbValue$3) * RGB$1.MAX);
return {
r: toSrgbFixed(rLinear),
g: toSrgbFixed(gLinear),
b: toSrgbFixed(bLinear),
};
}
function labaToRgba(laba) {
return {
a: laba.a,
...labToRgb(laba),
};
}
const labRegex = /lab\(\s*(\d+(\.\d+)?)%\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?\s*\)/i;
class LabColorManager {
accepts(input) {
return input.startsWith("lab");
}
handleColor(color) {
const colorValue = color.value, labColor = colorValue.lab ?? color.value;
if (!("l" in labColor) || !("aAxis" in labColor) || !("bAxis" in labColor)) {
return;
}
return labToRgb(labColor);
}
handleRangeColor(color) {
const colorValue = color.value, labColor = colorValue.lab ?? color.value;
if (!("l" in labColor) || !("aAxis" in labColor) || !("bAxis" in labColor)) {
return;
}
return labToRgb({
l: getRangeValue(labColor.l),
aAxis: getRangeValue(labColor.aAxis),
bAxis: getRangeValue(labColor.bAxis),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = labRegex.exec(input), indexes = {
l: 1,
aAxis: 3,
bAxis: 5,
a: 7,
}, defaultAlpha = 1;
return result
? labaToRgba({
a: result[indexes.a] ? parseAlpha(result[indexes.a]) : defaultAlpha,
aAxis: parseFloat(result[indexes.aAxis] ?? "0"),
bAxis: parseFloat(result[indexes.bAxis] ?? "0"),
l: parseFloat(result[indexes.l] ?? "0"),
})
: undefined;
}
}
async function loadLabColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("lab", new LabColorManager());
});
}
const RGB = {
MAX: 255,
}, WHITE_POINT_D65 = {
X: 0.95047,
Y: 1.0,
Z: 1.08883,
}, CIE = {
LAB_EPSILON_NUMERATOR: 216,
LAB_EPSILON_DENOMINATOR: 24389,
LAB_KAPPA_NUMERATOR: 24389,
LAB_KAPPA_DENOMINATOR: 27,
LAB_L_OFFSET: 16,
LAB_L_SCALE: 116,
LAB_A_SCALE: 500,
LAB_B_SCALE: 200,
}, LAB = {
EPSILON: CIE.LAB_EPSILON_NUMERATOR / CIE.LAB_EPSILON_DENOMINATOR,
KAPPA: CIE.LAB_KAPPA_NUMERATOR / CIE.LAB_KAPPA_DENOMINATOR,
L_OFFSET: CIE.LAB_L_OFFSET,
L_SCALE: CIE.LAB_L_SCALE,
A_SCALE: CIE.LAB_A_SCALE,
B_SCALE: CIE.LAB_B_SCALE,
}, XYZ_TO_LINEAR_RGB = {
r: { x: 3.2404542, y: -1.5371385, z: -0.4985314 },
g: { x: -0.969266, y: 1.8760108, z: 0.041556 },
b: { x: 0.0556434, y: -0.2040259, z: 1.0572252 },
}, SRGB$2 = {
GAMMA: 2.4,
LINEAR_THRESHOLD: 0.0031308,
LINEAR_SCALE: 12.92,
SCALE: 1.055,
OFFSET: 0.055,
}, minSrgbValue$2 = 0, maxSrgbValue$2 = 1;
function lchToRgb(lch) {
const L = lch.l, C = lch.c, hRad = degToRad(lch.h), a = C * Math.cos(hRad), b = C * Math.sin(hRad), fy = (L + LAB.L_OFFSET) / LAB.L_SCALE, fx = fy + a / LAB.A_SCALE, fz = fy - b / LAB.B_SCALE, cubic = 3, fx3 = fx ** cubic, fz3 = fz ** cubic, xr = fx3 > LAB.EPSILON ? fx3 : (LAB.L_SCALE * fx - LAB.L_OFFSET) / LAB.KAPPA, yr = L > LAB.KAPPA * LAB.EPSILON ? fy ** cubic : L / LAB.KAPPA, zr = fz3 > LAB.EPSILON ? fz3 : (LAB.L_SCALE * fz - LAB.L_OFFSET) / LAB.KAPPA, X = xr * WHITE_POINT_D65.X, Y = yr * WHITE_POINT_D65.Y, Z = zr * WHITE_POINT_D65.Z, rLinear = XYZ_TO_LINEAR_RGB.r.x * X + XYZ_TO_LINEAR_RGB.r.y * Y + XYZ_TO_LINEAR_RGB.r.z * Z, gLinear = XYZ_TO_LINEAR_RGB.g.x * X + XYZ_TO_LINEAR_RGB.g.y * Y + XYZ_TO_LINEAR_RGB.g.z * Z, bLinear = XYZ_TO_LINEAR_RGB.b.x * X + XYZ_TO_LINEAR_RGB.b.y * Y + XYZ_TO_LINEAR_RGB.b.z * Z, toSrgb = (x) => x <= SRGB$2.LINEAR_THRESHOLD
? SRGB$2.LINEAR_SCALE * x
: SRGB$2.SCALE * Math.pow(x, inverseFactorNumerator / SRGB$2.GAMMA) - SRGB$2.OFFSET, toSrgbFixed = num => Math.round(clamp(toSrgb(num), minSrgbValue$2, maxSrgbValue$2) * RGB.MAX);
return {
r: toSrgbFixed(rLinear),
g: toSrgbFixed(gLinear),
b: toSrgbFixed(bLinear),
};
}
function lchaToRgba(lcha) {
return {
a: lcha.a,
...lchToRgb(lcha),
};
}
const lchRegex = /lch\(\s*(\d+(\.\d+)?)%\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?\s*\)/i;
class LchColorManager {
accepts(input) {
return input.startsWith("lch");
}
handleColor(color) {
const colorValue = color.value, lchColor = colorValue.lch ?? color.value;
if (!("l" in lchColor) || !("c" in lchColor) || !("h" in lchColor)) {
return;
}
return lchToRgb(lchColor);
}
handleRangeColor(color) {
const colorValue = color.value, lchColor = colorValue.lch ?? color.value;
if (!("l" in lchColor) || !("c" in lchColor) || !("h" in lchColor)) {
return;
}
return lchToRgb({
l: getRangeValue(lchColor.l),
c: getRangeValue(lchColor.c),
h: getRangeValue(lchColor.h),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = lchRegex.exec(input), indexes = {
l: 1,
c: 3,
h: 5,
a: 7,
}, defaultAlpha = 1;
return result
? lchaToRgba({
a: result[indexes.a] ? parseAlpha(result[indexes.a]) : defaultAlpha,
c: parseFloat(result[indexes.c] ?? "0"),
h: parseFloat(result[indexes.h] ?? "0"),
l: parseFloat(result[indexes.l] ?? "0"),
})
: undefined;
}
}
async function loadLchColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("lch", new LchColorManager());
});
}
const defaultScale = 1, defaultLevyAlpha = 1.5;
class LevyPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = {
alpha: defaultLevyAlpha,
scale: defaultScale,
maxStep: 10,
};
}
generate(p) {
p.levy ??= {
angle: getRandom() * Math.PI * 2,
baseSpeed: p.velocity.length,
};
const opts = this.options, l = p.levy;
l.angle += (getRandom() - 0.5) * 0.2;
const u = Math.max(getRandom(), 1e-6), scale = opts.scale ?? defaultScale, levyAlpha = opts.alpha ?? defaultLevyAlpha;
let step = scale / Math.pow(u, 1 / levyAlpha);
if (opts.maxStep !== undefined) {
step = Math.min(step, opts.maxStep);
}
const speed = l.baseSpeed * step;
p.velocity.x = 0;
p.velocity.y = 0;
this.#res.length = speed;
this.#res.angle = l.angle;
return this.#res;
}
init() {
const source = this.#container.actualOptions.particles.move.path.options;
this.options.alpha = source["alpha"] ?? this.options.alpha;
this.options.scale = source["scale"] ?? this.options.scale;
this.options.maxStep = source["maxStep"] ?? this.options.maxStep;
}
reset(p) {
delete p.levy;
}
update() {
}
}
const levyPathName = "levyPathGenerator";
async function loadLevyPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(levyPathName, container => {
return Promise.resolve(new LevyPathGenerator(container));
});
});
}
const gradientPos = {
max: 1,
min: 0,
}, halfPI$2 = Math.PI * half$2, quarterPI = Math.PI * quarter$2, arcStart = 0;
const lightMode = "light";
function drawLight(container, context, mousePos) {
const lightOptions = container.actualOptions.interactivity?.modes.light?.area;
if (!lightOptions) {
return;
}
context.beginPath();
context.arc(mousePos.x, mousePos.y, lightOptions.radius, arcStart, doublePI);
const gradientAmbientLight = context.createRadialGradient(mousePos.x, mousePos.y, arcStart, mousePos.x, mousePos.y, lightOptions.radius), gradientRgb = container.canvas.mouseLight;
if (!gradientRgb?.start || !gradientRgb.stop) {
return;
}
gradientAmbientLight.addColorStop(gradientPos.min, getStyleFromRgb(gradientRgb.start, container.hdr, undefined, container.peakNits, container.hdrMode));
gradientAmbientLight.addColorStop(gradientPos.max, getStyleFromRgb(gradientRgb.stop, container.hdr, undefined, container.peakNits, container.hdrMode));
context.fillStyle = gradientAmbientLight;
context.fill();
}
function drawParticleShadow(container, context, particle, mousePos) {
const pos = particle.getPosition(), shadowOptions = container.actualOptions.interactivity?.modes.light?.shadow;
if (!shadowOptions) {
return;
}
const shadowRgb = particle.lightShadow;
if (!shadowRgb) {
return;
}
const radius = particle.getRadius(), sides = particle.sides, full = doublePI / sides, angle = -particle.getAngle() + quarterPI, factor = 1, dots = [];
for (let i = 0; i < sides; i++) {
dots.push({
x: pos.x + radius * Math.sin(angle + full * i) * factor,
y: pos.y + radius * Math.cos(angle + full * i) * factor,
});
}
const points = [], shadowLength = shadowOptions.length;
for (const dot of dots) {
const dotAngle = Math.atan2(mousePos.y - dot.y, mousePos.x - dot.x), end = {
x: dot.x + shadowLength * Math.sin(-dotAngle - halfPI$2),
y: dot.y + shadowLength * Math.cos(-dotAngle - halfPI$2),
};
points.push({
end: end,
start: dot,
});
}
const shadowColor = getStyleFromRgb(shadowRgb, container.hdr, undefined, container.peakNits, container.hdrMode), lastOffset = 1, firstPos = 0, last = points.length - lastOffset;
for (let i = last, n = 0; i >= firstPos; n = i--) {
context.beginPath();
const iPoint = points[i], nPoint = points[n];
if (!iPoint || !nPoint) {
continue;
}
context.moveTo(iPoint.start.x, iPoint.start.y);
context.lineTo(nPoint.start.x, nPoint.start.y);
context.lineTo(nPoint.end.x, nPoint.end.y);
context.lineTo(iPoint.end.x, iPoint.end.y);
context.fillStyle = shadowColor;
context.fill();
}
}
class LightGradient {
start = new OptionsColor();
stop = new OptionsColor();
constructor() {
this.start.value = "#ffffff";
this.stop.value = "#000000";
}
load(data) {
if (isNull(data)) {
return;
}
this.start = OptionsColor.create(this.start, data.start);
this.stop = OptionsColor.create(this.stop, data.stop);
}
}
class LightArea {
gradient = new LightGradient();
radius = 1000;
load(data) {
if (isNull(data)) {
return;
}
this.gradient.load(data.gradient);
loadProperty(this, "radius", data.radius);
}
}
class LightShadow {
color = new OptionsColor();
length = 2000;
constructor() {
this.color.value = "#000000";
}
load(data) {
if (isNull(data)) {
return;
}
this.color = OptionsColor.create(this.color, data.color);
loadProperty(this, "length", data.length);
}
}
class Light {
area = new LightArea();
shadow = new LightShadow();
load(data) {
if (isNull(data)) {
return;
}
this.area.load(data.area);
this.shadow.load(data.shadow);
}
}
class ExternalLighter extends ExternalInteractorBase {
maxDistance = 0;
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
}
clear() {
}
init() {
}
interact(interactivityData) {
const container = this.container, options = container.actualOptions, interactivity = interactivityData;
if (!options.interactivity?.events.onHover.enable || interactivity.status !== "pointermove") {
return;
}
const mousePos = interactivity.mouse.position;
if (!mousePos) {
return;
}
container.canvas.render.draw(ctx => {
drawLight(container, ctx, mousePos);
});
}
isEnabled(interactivityData, particle) {
const container = this.container, mouse = interactivityData.mouse, interactivity = particle?.interactivity ?? container.actualOptions.interactivity, events = interactivity?.events;
if (!(events?.onHover.enable && mouse.position)) {
return false;
}
const res = isInArray(lightMode, events.onHover.mode);
if (res && interactivity?.modes.light) {
const lightGradient = interactivity.modes.light.area.gradient;
container.canvas.mouseLight = {
start: rangeColorToRgb(this.#pluginManager, lightGradient.start),
stop: rangeColorToRgb(this.#pluginManager, lightGradient.stop),
};
}
return res;
}
loadModeOptions(options, ...sources) {
loadOptionProperty(options, "light", Light, ...sources);
}
reset() {
}
}
class ParticlesLighter extends ParticlesInteractorBase {
maxDistance;
#pluginManager;
constructor(pluginManager, container) {
super(container);
this.#pluginManager = pluginManager;
this.maxDistance = 0;
}
clear() {
}
init() {
}
interact(particle, interactivityData) {
const container = this.container, options = container.actualOptions;
if (!options.interactivity?.events.onHover.enable || interactivityData.status !== "pointermove") {
return;
}
const mousePos = interactivityData.mouse.position;
if (!mousePos) {
return;
}
container.canvas.render.draw(ctx => {
drawParticleShadow(container, ctx, particle, mousePos);
});
}
isEnabled(particle, interactivityData) {
const interactivity = particle.interactivity, mouse = interactivityData.mouse, events = interactivity.events;
if (!(events.onHover.enable && mouse.position)) {
return false;
}
const res = isInArray(lightMode, events.onHover.mode);
if (res && interactivity.modes.light) {
const shadowOptions = interactivity.modes.light.shadow;
particle.lightShadow = rangeColorToRgb(this.#pluginManager, shadowOptions.color);
}
return res;
}
reset() {
}
}
async function loadLightInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("externalLight", container => {
return Promise.resolve(new ExternalLighter(e.pluginManager, container));
});
e.pluginManager.addInteractor?.("particlesLight", container => {
return Promise.resolve(new ParticlesLighter(e.pluginManager, container));
});
});
}
const manualDefaultPosition = 50;
class ManualParticle {
options;
position;
load(data) {
if (isNull(data)) {
return;
}
if (data.position) {
this.position = {
x: data.position.x ?? manualDefaultPosition,
y: data.position.y ?? manualDefaultPosition,
mode: data.position.mode ?? exports.PixelMode.percent,
};
}
if (data.options) {
this.options = deepExtend({}, data.options);
}
}
}
class ManualParticlesPlugin {
id = "manual-particles";
async getPlugin(container) {
const { ManualParticlesPluginInstance } = await Promise.resolve().then(function () { return ManualParticlesPluginInstance$1; });
return new ManualParticlesPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
options.manualParticles ??= [];
if (source?.manualParticles) {
options.manualParticles = source.manualParticles.map(t => {
const tmp = new ManualParticle();
tmp.load(t);
return tmp;
});
}
}
needsPlugin(options) {
return !!options?.manualParticles?.length;
}
}
async function loadManualParticlesPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ManualParticlesPlugin());
});
}
const matrixChars = [
"ア",
"\u30A4",
"\u30A6",
"\u30A8",
"\u30AA",
"\u30AB",
"\u30AD",
"\u30AF",
"\u30B1",
"\u30B3",
"\u30B5",
"\u30B7",
"\u30B9",
"\u30BB",
"\u30BD",
"\u30BF",
"\u30C1",
"\u30C4",
"\u30C6",
"\u30C8",
"\u30CA",
"\u30CB",
"\u30CC",
"\u30CD",
"\u30CE",
"\u30CF",
"\u30D2",
"\u30D5",
"\u30D8",
"\u30DB",
"\u30DE",
"\u30DF",
"\u30E0",
"\u30E1",
"\u30E2",
"\u30E4",
"\u30E6",
"\u30E8",
"\u30E9",
"\u30EA",
"\u30EB",
"\u30EC",
"\u30ED",
"\u30EF",
"\u30F2",
"\u30F3",
"\u30AC",
"\u30AE",
"\u30B0",
"\u30B2",
"\u30B4",
"\u30B6",
"\u30B8",
"\u30BA",
"\u30BC",
"\u30BE",
"\u30C0",
"\u30C2",
"\u30C5",
"\u30C7",
"\u30C9",
"\u30D0",
"\u30D3",
"\u30D6",
"\u30D9",
"\u30DC",
"\u30D1",
"\u30D4",
"\u30D7",
"\u30DA",
"\u30DD",
"\u30A1",
"\u30A3",
"\u30A5",
"\u30A7",
"\u30A9",
"\u30E3",
"\u30E5",
"\u30E7",
"\u30C3",
"\u30FC",
"\u30F4",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"\uFF10",
"\uFF11",
"\uFF12",
"\uFF13",
"\uFF14",
"\uFF15",
"\uFF16",
"\uFF17",
"\uFF18",
"\uFF19",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"\u65E5",
"\u672C",
"\u4EBA",
"\u5927",
"\u4E2D",
"\u5C0F",
"\u4E0A",
"\u4E0B",
"\u5DE6",
"\u53F3",
"\u529B",
"\u6642",
"\u7A7A",
"\u751F",
"\u6B7B",
"\u7121",
"\u6709",
"!",
"@",
"#",
"$",
"%",
"&",
"*",
"+",
"-",
"=",
"?",
"/",
"\\",
"|",
"<",
">",
"{",
"}",
"[",
"]",
"(",
")",
];
const defaultMinInterval = 100, defaultMaxInterval = 2000, charCache = new WeakMap();
function randomChar() {
return matrixChars[Math.floor(getRandom() * matrixChars.length)] ?? " ";
}
function randomInterval(minInterval, maxInterval) {
return minInterval + getRandom() * (maxInterval - minInterval);
}
function getState(particle, minInterval, maxInterval) {
let state = charCache.get(particle);
if (!state) {
state = {
char: randomChar(),
elapsed: 0,
interval: randomInterval(minInterval, maxInterval),
};
charCache.set(particle, state);
}
return state;
}
function drawMatrix(data) {
const { context, radius, particle, delta, fill, stroke } = data, shapeData = particle.shapeData, minInterval = getRangeMin(shapeData?.interval ?? defaultMinInterval), maxInterval = getRangeMax(shapeData?.interval ?? defaultMaxInterval), state = getState(particle, minInterval, maxInterval);
state.elapsed += delta.value;
if (state.elapsed >= state.interval) {
state.char = randomChar();
state.elapsed -= state.interval;
state.interval = randomInterval(minInterval, maxInterval);
}
const fontSize = Math.round(radius * double);
context.font = `bold ${fontSize}px "Courier New", Courier, monospace`;
context.textAlign = "center";
context.textBaseline = "middle";
if (fill) {
context.fillText(state.char, originPoint.x, originPoint.y);
}
if (stroke) {
context.strokeText(state.char, originPoint.x, originPoint.y);
}
}
class MatrixDrawer {
draw(data) {
drawMatrix(data);
}
}
async function loadMatrixShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["matrix"], () => Promise.resolve(new MatrixDrawer()));
});
}
class MotionReduce {
factor = 4;
value = true;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "factor", data.factor);
loadProperty(this, "value", data.value);
}
}
class Motion {
disable = true;
reduce = new MotionReduce();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "disable", data.disable);
this.reduce.load(data.reduce);
}
}
class MotionPlugin {
id = "motion";
async getPlugin(container) {
const { MotionPluginInstance } = await Promise.resolve().then(function () { return MotionPluginInstance$1; });
return new MotionPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin()) {
return;
}
let motionOptions = options.motion;
if (!motionOptions?.load) {
options.motion = motionOptions = new Motion();
}
motionOptions.load(source?.motion);
}
needsPlugin() {
return true;
}
}
async function loadMotionPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new MotionPlugin());
});
}
const namedColors = new Map([
["aliceblue", { r: 240, g: 248, b: 255 }],
["antiquewhite", { r: 250, g: 235, b: 215 }],
["aqua", { r: 0, g: 255, b: 255 }],
["aquamarine", { r: 127, g: 255, b: 212 }],
["azure", { r: 240, g: 255, b: 255 }],
["beige", { r: 245, g: 245, b: 220 }],
["bisque", { r: 255, g: 228, b: 196 }],
["black", { r: 0, g: 0, b: 0 }],
["blanchedalmond", { r: 255, g: 235, b: 205 }],
["blue", { r: 0, g: 0, b: 255 }],
["blueviolet", { r: 138, g: 43, b: 226 }],
["brown", { r: 165, g: 42, b: 42 }],
["burlywood", { r: 222, g: 184, b: 135 }],
["cadetblue", { r: 95, g: 158, b: 160 }],
["chartreuse", { r: 127, g: 255, b: 0 }],
["chocolate", { r: 210, g: 105, b: 30 }],
["coral", { r: 255, g: 127, b: 80 }],
["cornflowerblue", { r: 100, g: 149, b: 237 }],
["cornsilk", { r: 255, g: 248, b: 220 }],
["crimson", { r: 220, g: 20, b: 60 }],
["cyan", { r: 0, g: 255, b: 255 }],
["darkblue", { r: 0, g: 0, b: 139 }],
["darkcyan", { r: 0, g: 139, b: 139 }],
["darkgoldenrod", { r: 184, g: 134, b: 11 }],
["darkgray", { r: 169, g: 169, b: 169 }],
["darkgreen", { r: 0, g: 100, b: 0 }],
["darkgrey", { r: 169, g: 169, b: 169 }],
["darkkhaki", { r: 189, g: 183, b: 107 }],
["darkmagenta", { r: 139, g: 0, b: 139 }],
["darkolivegreen", { r: 85, g: 107, b: 47 }],
["darkorange", { r: 255, g: 140, b: 0 }],
["darkorchid", { r: 153, g: 50, b: 204 }],
["darkred", { r: 139, g: 0, b: 0 }],
["darksalmon", { r: 233, g: 150, b: 122 }],
["darkseagreen", { r: 143, g: 188, b: 143 }],
["darkslateblue", { r: 72, g: 61, b: 139 }],
["darkslategray", { r: 47, g: 79, b: 79 }],
["darkslategrey", { r: 47, g: 79, b: 79 }],
["darkturquoise", { r: 0, g: 206, b: 209 }],
["darkviolet", { r: 148, g: 0, b: 211 }],
["deeppink", { r: 255, g: 20, b: 147 }],
["deepskyblue", { r: 0, g: 191, b: 255 }],
["dimgray", { r: 105, g: 105, b: 105 }],
["dimgrey", { r: 105, g: 105, b: 105 }],
["dodgerblue", { r: 30, g: 144, b: 255 }],
["firebrick", { r: 178, g: 34, b: 34 }],
["floralwhite", { r: 255, g: 250, b: 240 }],
["forestgreen", { r: 34, g: 139, b: 34 }],
["fuchsia", { r: 255, g: 0, b: 255 }],
["gainsboro", { r: 220, g: 220, b: 220 }],
["ghostwhite", { r: 248, g: 248, b: 255 }],
["gold", { r: 255, g: 215, b: 0 }],
["goldenrod", { r: 218, g: 165, b: 32 }],
["gray", { r: 128, g: 128, b: 128 }],
["green", { r: 0, g: 128, b: 0 }],
["greenyellow", { r: 173, g: 255, b: 47 }],
["grey", { r: 128, g: 128, b: 128 }],
["honeydew", { r: 240, g: 255, b: 240 }],
["hotpink", { r: 255, g: 105, b: 180 }],
["indianred", { r: 205, g: 92, b: 92 }],
["indigo", { r: 75, g: 0, b: 130 }],
["ivory", { r: 255, g: 255, b: 240 }],
["khaki", { r: 240, g: 230, b: 140 }],
["lavender", { r: 230, g: 230, b: 250 }],
["lavenderblush", { r: 255, g: 240, b: 245 }],
["lawngreen", { r: 124, g: 252, b: 0 }],
["lemonchiffon", { r: 255, g: 250, b: 205 }],
["lightblue", { r: 173, g: 216, b: 230 }],
["lightcoral", { r: 240, g: 128, b: 128 }],
["lightcyan", { r: 224, g: 255, b: 255 }],
["lightgoldenrodyellow", { r: 250, g: 250, b: 210 }],
["lightgray", { r: 211, g: 211, b: 211 }],
["lightgreen", { r: 144, g: 238, b: 144 }],
["lightgrey", { r: 211, g: 211, b: 211 }],
["lightpink", { r: 255, g: 182, b: 193 }],
["lightsalmon", { r: 255, g: 160, b: 122 }],
["lightseagreen", { r: 32, g: 178, b: 170 }],
["lightskyblue", { r: 135, g: 206, b: 250 }],
["lightslategray", { r: 119, g: 136, b: 153 }],
["lightslategrey", { r: 119, g: 136, b: 153 }],
["lightsteelblue", { r: 176, g: 196, b: 222 }],
["lightyellow", { r: 255, g: 255, b: 224 }],
["lime", { r: 0, g: 255, b: 0 }],
["limegreen", { r: 50, g: 205, b: 50 }],
["linen", { r: 250, g: 240, b: 230 }],
["magenta", { r: 255, g: 0, b: 255 }],
["maroon", { r: 128, g: 0, b: 0 }],
["mediumaquamarine", { r: 102, g: 205, b: 170 }],
["mediumblue", { r: 0, g: 0, b: 205 }],
["mediumorchid", { r: 186, g: 85, b: 211 }],
["mediumpurple", { r: 147, g: 112, b: 219 }],
["mediumseagreen", { r: 60, g: 179, b: 113 }],
["mediumslateblue", { r: 123, g: 104, b: 238 }],
["mediumspringgreen", { r: 0, g: 250, b: 154 }],
["mediumturquoise", { r: 72, g: 209, b: 204 }],
["mediumvioletred", { r: 199, g: 21, b: 133 }],
["midnightblue", { r: 25, g: 25, b: 112 }],
["mintcream", { r: 245, g: 255, b: 250 }],
["mistyrose", { r: 255, g: 228, b: 225 }],
["moccasin", { r: 255, g: 228, b: 181 }],
["navajowhite", { r: 255, g: 222, b: 173 }],
["navy", { r: 0, g: 0, b: 128 }],
["oldlace", { r: 253, g: 245, b: 230 }],
["olive", { r: 128, g: 128, b: 0 }],
["olivedrab", { r: 107, g: 142, b: 35 }],
["orange", { r: 255, g: 165, b: 0 }],
["orangered", { r: 255, g: 69, b: 0 }],
["orchid", { r: 218, g: 112, b: 214 }],
["palegoldenrod", { r: 238, g: 232, b: 170 }],
["palegreen", { r: 152, g: 251, b: 152 }],
["paleturquoise", { r: 175, g: 238, b: 238 }],
["palevioletred", { r: 219, g: 112, b: 147 }],
["papayawhip", { r: 255, g: 239, b: 213 }],
["peachpuff", { r: 255, g: 218, b: 185 }],
["peru", { r: 205, g: 133, b: 63 }],
["pink", { r: 255, g: 192, b: 203 }],
["plum", { r: 221, g: 160, b: 221 }],
["powderblue", { r: 176, g: 224, b: 230 }],
["purple", { r: 128, g: 0, b: 128 }],
["rebeccapurple", { r: 102, g: 51, b: 153 }],
["red", { r: 255, g: 0, b: 0 }],
["rosybrown", { r: 188, g: 143, b: 143 }],
["royalblue", { r: 65, g: 105, b: 225 }],
["saddlebrown", { r: 139, g: 69, b: 19 }],
["salmon", { r: 250, g: 128, b: 114 }],
["sandybrown", { r: 244, g: 164, b: 96 }],
["seagreen", { r: 46, g: 139, b: 87 }],
["seashell", { r: 255, g: 245, b: 238 }],
["sienna", { r: 160, g: 82, b: 45 }],
["silver", { r: 192, g: 192, b: 192 }],
["skyblue", { r: 135, g: 206, b: 235 }],
["slateblue", { r: 106, g: 90, b: 205 }],
["slategray", { r: 112, g: 128, b: 144 }],
["slategrey", { r: 112, g: 128, b: 144 }],
["snow", { r: 255, g: 250, b: 250 }],
["springgreen", { r: 0, g: 255, b: 127 }],
["steelblue", { r: 70, g: 130, b: 180 }],
["tan", { r: 210, g: 180, b: 140 }],
["teal", { r: 0, g: 128, b: 128 }],
["thistle", { r: 216, g: 191, b: 216 }],
["tomato", { r: 255, g: 99, b: 71 }],
["turquoise", { r: 64, g: 224, b: 208 }],
["violet", { r: 238, g: 130, b: 238 }],
["wheat", { r: 245, g: 222, b: 179 }],
["white", { r: 255, g: 255, b: 255 }],
["whitesmoke", { r: 245, g: 245, b: 245 }],
["yellow", { r: 255, g: 255, b: 0 }],
["yellowgreen", { r: 154, g: 205, b: 50 }],
]);
class NamedColorManager {
accepts(input) {
return namedColors.has(input.toLowerCase());
}
handleColor(color) {
return this.#parseString(color.value);
}
handleRangeColor(color) {
return this.#parseString(color.value);
}
parseString(input) {
return this.#parseString(input);
}
#parseString(input) {
if (!input || typeof input !== "string") {
return undefined;
}
const rgbColor = namedColors.get(input.toLowerCase());
if (!rgbColor) {
getLogger().error("Color not found", input);
return undefined;
}
return { ...rgbColor, a: 1 };
}
}
async function loadNamedColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("named", new NamedColorManager());
});
}
const OKLAB_LMS$1 = {
l: { L: 1, a: 0.3963377774, b: 0.2158037573 },
m: { L: 1, a: -0.1055613458, b: -0.0638541728 },
s: { L: 1, a: -0.0894841775, b: -1.291485548 },
}, LMS_TO_LINEAR_RGB$1 = {
r: { l: 4.0767416621, m: -3.3077115913, s: 0.2309699292 },
g: { l: -1.2684380046, m: 2.6097574011, s: -0.3413193965 },
b: { l: -0.0041960863, m: -0.7034186147, s: 1.707614701 },
}, SRGB$1 = {
GAMMA: 2.4,
LINEAR_THRESHOLD: 0.0031308,
LINEAR_SCALE: 12.92,
OFFSET: 0.055,
SCALE: 1.055,
}, minSrgbValue$1 = 0, maxSrgbValue$1 = 1;
function oklabToRgb(oklab) {
const L = oklab.l / percentDenominator, a = oklab.aAxis, b = oklab.bAxis, l_ = OKLAB_LMS$1.l.L * L + OKLAB_LMS$1.l.a * a + OKLAB_LMS$1.l.b * b, m_ = OKLAB_LMS$1.m.L * L + OKLAB_LMS$1.m.a * a + OKLAB_LMS$1.m.b * b, s_ = OKLAB_LMS$1.s.L * L + OKLAB_LMS$1.s.a * a + OKLAB_LMS$1.s.b * b, cubic = 3, l = l_ ** cubic, m = m_ ** cubic, s = s_ ** cubic, rLinear = LMS_TO_LINEAR_RGB$1.r.l * l + LMS_TO_LINEAR_RGB$1.r.m * m + LMS_TO_LINEAR_RGB$1.r.s * s, gLinear = LMS_TO_LINEAR_RGB$1.g.l * l + LMS_TO_LINEAR_RGB$1.g.m * m + LMS_TO_LINEAR_RGB$1.g.s * s, bLinear = LMS_TO_LINEAR_RGB$1.b.l * l + LMS_TO_LINEAR_RGB$1.b.m * m + LMS_TO_LINEAR_RGB$1.b.s * s, toSrgb = (x) => x <= SRGB$1.LINEAR_THRESHOLD
? SRGB$1.LINEAR_SCALE * x
: SRGB$1.SCALE * Math.pow(x, inverseFactorNumerator / SRGB$1.GAMMA) - SRGB$1.OFFSET, toSrgbFixed = num => Math.round(clamp(toSrgb(num), minSrgbValue$1, maxSrgbValue$1) * rgbMax);
return {
r: toSrgbFixed(rLinear),
g: toSrgbFixed(gLinear),
b: toSrgbFixed(bLinear),
};
}
function oklabaToRgba(oklaba) {
return {
a: oklaba.a,
...oklabToRgb(oklaba),
};
}
const oklabRegex = /oklab\(\s*(\d+(?:\.\d+)?)(%?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?\s*\)/i;
class OklabColorManager {
accepts(input) {
return input.startsWith("oklab");
}
handleColor(color) {
const colorValue = color.value, oklabColor = colorValue.oklab ?? color.value;
if (!("l" in oklabColor) || !("aAxis" in oklabColor) || !("bAxis" in oklabColor)) {
return;
}
return oklabToRgb(oklabColor);
}
handleRangeColor(color) {
const colorValue = color.value, oklabColor = colorValue.oklab ?? color.value;
if (!("l" in oklabColor) || !("aAxis" in oklabColor) || !("bAxis" in oklabColor)) {
return;
}
return oklabToRgb({
l: getRangeValue(oklabColor.l),
aAxis: getRangeValue(oklabColor.aAxis),
bAxis: getRangeValue(oklabColor.bAxis),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = oklabRegex.exec(input), indexes = {
l: 1,
lPercent: 2,
aAxis: 3,
bAxis: 4,
a: 5,
}, defaultAlpha = 1;
if (!result) {
return undefined;
}
const rawL = parseFloat(result[indexes.l] ?? "0");
if (result[indexes.lPercent]) {
if (rawL < none || rawL > percentDenominator) {
return undefined;
}
}
else {
if (rawL < none || rawL > identity$3) {
return undefined;
}
}
return oklabaToRgba({
a: result[indexes.a] ? parseAlpha(result[indexes.a]) : defaultAlpha,
l: rawL * (result[indexes.lPercent] ? identity$3 : percentDenominator),
aAxis: parseFloat(result[indexes.aAxis] ?? "0"),
bAxis: parseFloat(result[indexes.bAxis] ?? "0"),
});
}
}
async function loadOklabColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("oklab", new OklabColorManager());
});
}
const OKLAB_LMS = {
l: { L: 1, a: 0.3963377774, b: 0.2158037573 },
m: { L: 1, a: -0.1055613458, b: -0.0638541728 },
s: { L: 1, a: -0.0894841775, b: -1.291485548 },
}, LMS_TO_LINEAR_RGB = {
r: { l: 4.0767416621, m: -3.3077115913, s: 0.2309699292 },
g: { l: -1.2684380046, m: 2.6097574011, s: -0.3413193965 },
b: { l: -0.0041960863, m: -0.7034186147, s: 1.707614701 },
}, SRGB = {
GAMMA: 2.4,
LINEAR_THRESHOLD: 0.0031308,
LINEAR_SCALE: 12.92,
SCALE: 1.055,
OFFSET: 0.055,
}, minSrgbValue = 0, maxSrgbValue = 1;
function oklchToRgb(oklch) {
const L = oklch.l / percentDenominator, C = oklch.c, hRad = degToRad(oklch.h), a = C * Math.cos(hRad), b = C * Math.sin(hRad), l_ = OKLAB_LMS.l.L * L + OKLAB_LMS.l.a * a + OKLAB_LMS.l.b * b, m_ = OKLAB_LMS.m.L * L + OKLAB_LMS.m.a * a + OKLAB_LMS.m.b * b, s_ = OKLAB_LMS.s.L * L + OKLAB_LMS.s.a * a + OKLAB_LMS.s.b * b, cubic = 3, l = l_ ** cubic, m = m_ ** cubic, s = s_ ** cubic, rLinear = LMS_TO_LINEAR_RGB.r.l * l + LMS_TO_LINEAR_RGB.r.m * m + LMS_TO_LINEAR_RGB.r.s * s, gLinear = LMS_TO_LINEAR_RGB.g.l * l + LMS_TO_LINEAR_RGB.g.m * m + LMS_TO_LINEAR_RGB.g.s * s, bLinear = LMS_TO_LINEAR_RGB.b.l * l + LMS_TO_LINEAR_RGB.b.m * m + LMS_TO_LINEAR_RGB.b.s * s, toSrgb = (x) => x <= SRGB.LINEAR_THRESHOLD
? SRGB.LINEAR_SCALE * x
: SRGB.SCALE * Math.pow(x, inverseFactorNumerator / SRGB.GAMMA) - SRGB.OFFSET, toSrgbFixed = num => Math.round(clamp(toSrgb(num), minSrgbValue, maxSrgbValue) * rgbMax);
return {
r: toSrgbFixed(rLinear),
g: toSrgbFixed(gLinear),
b: toSrgbFixed(bLinear),
};
}
function oklchaToRgba(oklcha) {
return {
a: oklcha.a,
...oklchToRgb(oklcha),
};
}
const oklchRegex = /oklch\(\s*(\d+(?:\.\d+)?)(%?)\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)(°)?(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?\s*\)/i;
class OklchColorManager {
accepts(input) {
return input.startsWith("oklch");
}
handleColor(color) {
const colorValue = color.value, oklchColor = colorValue.oklch ?? color.value;
if (!("l" in oklchColor) || !("c" in oklchColor) || !("h" in oklchColor)) {
return;
}
return oklchToRgb(oklchColor);
}
handleRangeColor(color) {
const colorValue = color.value, oklchColor = colorValue.oklch ?? color.value;
if (!("l" in oklchColor) || !("c" in oklchColor) || !("h" in oklchColor)) {
return;
}
return oklchToRgb({
l: getRangeValue(oklchColor.l),
c: getRangeValue(oklchColor.c),
h: getRangeValue(oklchColor.h),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = oklchRegex.exec(input), indexes = {
l: 1,
lPercent: 2,
c: 3,
h: 4,
a: 6,
}, defaultAlpha = 1;
if (!result) {
return undefined;
}
const rawL = parseFloat(result[indexes.l] ?? "0");
if (result[indexes.lPercent]) {
if (rawL < none || rawL > percentDenominator) {
return undefined;
}
}
else {
if (rawL < none || rawL > identity$3) {
return undefined;
}
}
return oklchaToRgba({
a: result[indexes.a] ? parseAlpha(result[indexes.a]) : defaultAlpha,
c: parseFloat(result[indexes.c] ?? "0"),
h: parseFloat(result[indexes.h] ?? "0"),
l: rawL * (result[indexes.lPercent] ? identity$3 : percentDenominator),
});
}
}
async function loadOklchColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("oklch", new OklchColorManager());
});
}
class OrbitRotation extends ValueWithRandom {
value = 45;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
}
}
class Orbit {
animation = new AnimationOptions();
color;
enable = false;
opacity = 1;
radius;
rotation = new OrbitRotation();
width = 1;
load(data) {
if (isNull(data)) {
return;
}
this.animation.load(data.animation);
this.rotation.load(data.rotation);
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "opacity", data.opacity);
loadRangeProperty(this, "width", data.width);
loadRangeProperty(this, "radius", data.radius);
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
}
}
var OrbitType;
(function (OrbitType) {
OrbitType["front"] = "front";
OrbitType["back"] = "back";
})(OrbitType || (OrbitType = {}));
const minWidth$3 = 0;
function drawEllipse(container, context, particle, fillColorValue, radius, opacity, width, rotation, start, end) {
if (width <= minWidth$3) {
return;
}
const pos = particle.getPosition();
if (fillColorValue) {
context.strokeStyle = getStyleFromHsl(fillColorValue, container.hdr, opacity, container.peakNits, container.hdrMode);
}
context.lineWidth = width;
const rotationRadian = degToRad(rotation);
context.beginPath();
context.ellipse(pos.x, pos.y, radius * half$2, radius * double, rotationRadian, start, end);
context.stroke();
}
const defaultOrbitSpeed = 0, halfPI$1 = Math.PI * half$2, piAndAHalf = Math.PI + halfPI$1, startAngle = 0, defaultWidth$1 = 1, defaultRotation$1 = 0;
class OrbitUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
afterDraw(particle) {
const orbitOptions = particle.options.orbit;
if (orbitOptions?.enable) {
this.drawOrbit(particle, OrbitType.front);
}
}
beforeDraw(particle) {
const orbitOptions = particle.options.orbit;
if (orbitOptions?.enable) {
this.drawOrbit(particle, OrbitType.back);
}
}
drawOrbit(particle, type) {
const container = this.#container;
let start, end;
switch (type) {
case OrbitType.back:
start = halfPI$1;
end = piAndAHalf;
break;
case OrbitType.front:
start = piAndAHalf;
end = halfPI$1;
break;
default:
start = startAngle;
end = doublePI;
}
container.canvas.render.draw(ctx => {
drawEllipse(container, ctx, particle, particle.orbitColor ?? particle.getFillColor(), particle.retina.orbitRadius ?? container.retina.orbitRadius ?? particle.getRadius(), particle.orbitOpacity ?? defaultOpacity$3, particle.orbitWidth ?? defaultWidth$1, (particle.orbitRotation ?? defaultRotation$1) * container.retina.pixelRatio, start, end);
});
}
init(particle) {
const container = this.#container, particlesOptions = particle.options, orbitOptions = particlesOptions.orbit;
if (!orbitOptions?.enable) {
return;
}
particle.orbitRotation = getRangeValue(orbitOptions.rotation.value);
particle.orbitColor = rangeColorToHsl(this.#pluginManager, orbitOptions.color);
particle.retina.orbitRadius =
orbitOptions.radius !== undefined ? getRangeValue(orbitOptions.radius) * container.retina.pixelRatio : undefined;
container.retina.orbitRadius = particle.retina.orbitRadius;
particle.orbitAnimationSpeed = orbitOptions.animation.enable
? getRangeValue(orbitOptions.animation.speed)
: defaultOrbitSpeed;
particle.orbitWidth = getRangeValue(orbitOptions.width);
particle.orbitOpacity = getRangeValue(orbitOptions.opacity);
}
isEnabled(particle) {
const orbitAnimations = particle.options.orbit?.animation;
return !particle.destroyed && !particle.spawning && !!orbitAnimations?.enable;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "orbit", Orbit, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
particle.orbitRotation ??= defaultRotation$1;
particle.orbitRotation += (particle.orbitAnimationSpeed ?? defaultOrbitSpeed / doublePI) * delta.factor;
}
}
async function loadOrbitUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("orbit", container => {
return Promise.resolve(new OrbitUpdater(e.pluginManager, container));
});
});
}
const minSpawnRate = 0, defaultSpawnRate = 1, defaultSpawnQuantity = 1;
class ParticlesDrawer {
#container;
constructor(container) {
this.#container = container;
}
drawAfter(data) {
const { particle } = data, container = this.#container;
if (!particle.particlesNextSpawn) {
return;
}
const currentTime = performance.now();
if (currentTime < particle.particlesNextSpawn) {
return;
}
particle.particlesNextSpawn =
currentTime + (particle.particlesSpawnRate ?? defaultSpawnRate * millisecondsToSeconds$1);
const quantity = particle.particlesSpawnQuantity ?? defaultSpawnQuantity, pOptions = deepExtend({}, particle.particlesData?.spawn?.particles), pos = { ...particle.getPosition() };
for (let i = 0; i < quantity; i++) {
container.particles.addParticle(pos, pOptions);
}
}
particleInit(_container, particle) {
const effectData = particle.effectData, spawnRate = getRangeValue(effectData?.spawn?.rate?.delay ?? defaultSpawnRate);
particle.particlesSpawnQuantity = getRangeValue(effectData?.spawn?.rate?.quantity ?? defaultSpawnQuantity);
particle.particlesSpawnRate = (spawnRate > minSpawnRate ? spawnRate : defaultSpawnRate) * millisecondsToSeconds$1;
particle.particlesNextSpawn = performance.now() + particle.particlesSpawnRate;
particle.particlesData = effectData;
}
}
async function loadParticlesEffect(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addEffect("particles", container => {
return Promise.resolve(new ParticlesDrawer(container));
});
});
}
class ParticlesRepulse extends ValueWithRandom {
distance = 1;
duration = 1;
enabled = false;
factor = 1;
speed = 1;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "enabled", data.enabled);
loadRangeProperty(this, "distance", data.distance);
loadRangeProperty(this, "duration", data.duration);
loadRangeProperty(this, "factor", data.factor);
loadRangeProperty(this, "speed", data.speed);
}
}
class Repulser extends ParticlesInteractorBase {
#maxDistance;
#normVec;
#velocityVec;
constructor(container) {
super(container);
this.#maxDistance = 0;
this.#normVec = Vector.origin;
this.#velocityVec = Vector.origin;
}
get maxDistance() {
return this.#maxDistance;
}
clear() {
}
init() {
}
interact(p1) {
const container = this.container;
if (!p1.repulse) {
const repulseOpt1 = p1.options.repulse;
if (!repulseOpt1) {
return;
}
const repulseDistance = getRangeValue(repulseOpt1.distance);
if (repulseDistance > this.maxDistance) {
this.#maxDistance = repulseDistance;
}
p1.repulse = {
distance: repulseDistance * container.retina.pixelRatio,
speed: getRangeValue(repulseOpt1.speed),
factor: getRangeValue(repulseOpt1.factor),
};
}
const pos1 = p1.getPosition(), query = container.particles.grid.queryCircle(pos1, p1.repulse.distance), p1DistanceFactor = identity$3 / p1.repulse.distance;
for (const p2 of query) {
if (p1 === p2 || p2.destroyed) {
continue;
}
const pos2 = p2.getPosition(), { dx, dy, distance } = getDistances(pos2, pos1), distanceFactor = identity$3 / distance, velocity = p1.repulse.speed * p1.repulse.factor;
if (distance > minDistance$7) {
const repulseFactor = clamp((identity$3 - Math.pow(distance * p1DistanceFactor, squareExp)) * velocity, minVelocity, velocity) *
distanceFactor;
this.#normVec.x = dx * repulseFactor;
this.#normVec.y = dy * repulseFactor;
p2.position.addTo(this.#normVec);
}
else {
this.#velocityVec.x = velocity;
this.#velocityVec.y = velocity;
p2.position.addTo(this.#velocityVec);
}
}
}
isEnabled(particle) {
return particle.options.repulse?.enabled ?? false;
}
loadParticlesOptions(options, ...sources) {
loadOptionProperty(options, "repulse", ParticlesRepulse, ...sources);
}
reset() {
}
}
async function loadParticlesRepulseInteraction(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureInteractivityPluginLoaded(e);
e.pluginManager.addInteractor?.("particlesRepulse", container => {
return Promise.resolve(new Repulser(container));
});
});
}
class PathDrawer {
draw(data) {
const { context, particle, radius } = data;
if (!particle.pathData) {
return;
}
drawPath(context, radius, particle.pathData);
}
particleInit(_container, particle) {
const shape = particle.shapeData;
if (!shape) {
return;
}
particle.pathData = deepExtend({}, shape);
}
}
async function loadPathShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["path"], () => Promise.resolve(new PathDrawer()));
});
}
class Grad {
w;
x;
y;
z;
constructor(x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
dot2(x, y) {
return this.x * x + this.y * y;
}
dot3(x, y, z) {
return this.dot2(x, y) + this.z * z;
}
dot4(x, y, z, w) {
return this.dot3(x, y, z) + this.w * w;
}
}
class PerlinNoise {
#grad4;
#gradP;
#p;
#perm;
constructor() {
this.#grad4 = [
new Grad(0, 1, 1, 1),
new Grad(0, 1, 1, -1),
new Grad(0, 1, -1, 1),
new Grad(0, 1, -1, -1),
new Grad(0, -1, 1, 1),
new Grad(0, -1, 1, -1),
new Grad(0, -1, -1, 1),
new Grad(0, -1, -1, -1),
new Grad(1, 0, 1, 1),
new Grad(1, 0, 1, -1),
new Grad(1, 0, -1, 1),
new Grad(1, 0, -1, -1),
new Grad(-1, 0, 1, 1),
new Grad(-1, 0, 1, -1),
new Grad(-1, 0, -1, 1),
new Grad(-1, 0, -1, -1),
new Grad(1, 1, 0, 1),
new Grad(1, 1, 0, -1),
new Grad(1, -1, 0, 1),
new Grad(1, -1, 0, -1),
new Grad(-1, 1, 0, 1),
new Grad(-1, 1, 0, -1),
new Grad(-1, -1, 0, 1),
new Grad(-1, -1, 0, -1),
new Grad(1, 1, 1, 0),
new Grad(1, 1, -1, 0),
new Grad(1, -1, 1, 0),
new Grad(1, -1, -1, 0),
new Grad(-1, 1, 1, 0),
new Grad(-1, 1, -1, 0),
new Grad(-1, -1, 1, 0),
new Grad(-1, -1, -1, 0),
];
this.#p = [
151,
160,
137,
91,
90,
15,
131,
13,
201,
95,
96,
53,
194,
233,
7,
225,
140,
36,
103,
30,
69,
142,
8,
99,
37,
240,
21,
10,
23,
190,
6,
148,
247,
120,
234,
75,
0,
26,
197,
62,
94,
252,
219,
203,
117,
35,
11,
32,
57,
177,
33,
88,
237,
149,
56,
87,
174,
20,
125,
136,
171,
168,
68,
175,
74,
165,
71,
134,
139,
48,
27,
166,
77,
146,
158,
231,
83,
111,
229,
122,
60,
211,
133,
230,
220,
105,
92,
41,
55,
46,
245,
40,
244,
102,
143,
54,
65,
25,
63,
161,
1,
216,
80,
73,
209,
76,
132,
187,
208,
89,
18,
169,
200,
196,
135,
130,
116,
188,
159,
86,
164,
100,
109,
198,
173,
186,
3,
64,
52,
217,
226,
250,
124,
123,
5,
202,
38,
147,
118,
126,
255,
82,
85,
212,
207,
206,
59,
227,
47,
16,
58,
17,
182,
189,
28,
42,
223,
183,
170,
213,
119,
248,
152,
2,
44,
154,
163,
70,
221,
153,
101,
155,
167,
43,
172,
9,
129,
22,
39,
253,
19,
98,
108,
110,
79,
113,
224,
232,
178,
185,
112,
104,
218,
246,
97,
228,
251,
34,
242,
193,
238,
210,
144,
12,
191,
179,
162,
241,
81,
51,
145,
235,
249,
14,
239,
107,
49,
192,
214,
31,
181,
199,
106,
157,
184,
84,
204,
176,
115,
121,
50,
45,
127,
4,
150,
254,
138,
236,
205,
93,
222,
114,
67,
29,
24,
72,
243,
141,
128,
195,
78,
66,
215,
61,
156,
180,
];
this.#gradP = new Array(512);
this.#perm = new Array(512);
}
noise2d(x, y) {
const gradP = this.#gradP, perm = this.#perm;
let X = Math.floor(x), Y = Math.floor(y);
x -= X;
y -= Y;
X &= 255;
Y &= 255;
const n00 = gradP[X + perm[Y]].dot2(x, y), n01 = gradP[X + perm[Y + 1]].dot2(x, y - 1), n10 = gradP[X + 1 + perm[Y]].dot2(x - 1, y), n11 = gradP[X + 1 + perm[Y + 1]].dot2(x - 1, y - 1), u = this.#fade(x);
return this.#lerp(this.#lerp(n00, n10, u), this.#lerp(n01, n11, u), this.#fade(y));
}
noise3d(x, y, z) {
const gradP = this.#gradP, perm = this.#perm;
let X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z);
x = x - X;
y = y - Y;
z = z - Z;
X = X & 255;
Y = Y & 255;
Z = Z & 255;
const n000 = gradP[X + perm[Y + perm[Z]]].dot3(x, y, z), n001 = gradP[X + perm[Y + perm[Z + 1]]].dot3(x, y, z - 1), n010 = gradP[X + perm[Y + 1 + perm[Z]]].dot3(x, y - 1, z), n011 = gradP[X + perm[Y + 1 + perm[Z + 1]]].dot3(x, y - 1, z - 1), n100 = gradP[X + 1 + perm[Y + perm[Z]]].dot3(x - 1, y, z), n101 = gradP[X + 1 + perm[Y + perm[Z + 1]]].dot3(x - 1, y, z - 1), n110 = gradP[X + 1 + perm[Y + 1 + perm[Z]]].dot3(x - 1, y - 1, z), n111 = gradP[X + 1 + perm[Y + 1 + perm[Z + 1]]].dot3(x - 1, y - 1, z - 1), u = this.#fade(x), v = this.#fade(y), w = this.#fade(z);
return this.#lerp(this.#lerp(this.#lerp(n000, n100, u), this.#lerp(n001, n101, u), w), this.#lerp(this.#lerp(n010, n110, u), this.#lerp(n011, n111, u), w), v);
}
noise4d(x, y, z, w) {
const gradP = this.#gradP, perm = this.#perm;
let X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z), W = Math.floor(w);
x -= X;
y -= Y;
z -= Z;
w -= W;
X &= 255;
Y &= 255;
Z &= 255;
W &= 255;
const u = this.#fade(x), v = this.#fade(y), s = this.#fade(z), t = this.#fade(w), gi = (i, j, k, l) => gradP[X + i + perm[Y + j + perm[Z + k + perm[W + l]]]], n0000 = gi(0, 0, 0, 0).dot4(x, y, z, w), n0001 = gi(0, 0, 0, 1).dot4(x, y, z, w - 1), n0010 = gi(0, 0, 1, 0).dot4(x, y, z - 1, w), n0011 = gi(0, 0, 1, 1).dot4(x, y, z - 1, w - 1), n0100 = gi(0, 1, 0, 0).dot4(x, y - 1, z, w), n0101 = gi(0, 1, 0, 1).dot4(x, y - 1, z, w - 1), n0110 = gi(0, 1, 1, 0).dot4(x, y - 1, z - 1, w), n0111 = gi(0, 1, 1, 1).dot4(x, y - 1, z - 1, w - 1), n1000 = gi(1, 0, 0, 0).dot4(x - 1, y, z, w), n1001 = gi(1, 0, 0, 1).dot4(x - 1, y, z, w - 1), n1010 = gi(1, 0, 1, 0).dot4(x - 1, y, z - 1, w), n1011 = gi(1, 0, 1, 1).dot4(x - 1, y, z - 1, w - 1), n1100 = gi(1, 1, 0, 0).dot4(x - 1, y - 1, z, w), n1101 = gi(1, 1, 0, 1).dot4(x - 1, y - 1, z, w - 1), n1110 = gi(1, 1, 1, 0).dot4(x - 1, y - 1, z - 1, w), n1111 = gi(1, 1, 1, 1).dot4(x - 1, y - 1, z - 1, w - 1), x00 = this.#lerp(n0000, n1000, u), x01 = this.#lerp(n0001, n1001, u), x10 = this.#lerp(n0010, n1010, u), x11 = this.#lerp(n0011, n1011, u), y00 = this.#lerp(x00, x10, s), y01 = this.#lerp(x01, x11, s), x20 = this.#lerp(n0100, n1100, u), x21 = this.#lerp(n0101, n1101, u), x30 = this.#lerp(n0110, n1110, u), x31 = this.#lerp(n0111, n1111, u), y10 = this.#lerp(x20, x30, s), y11 = this.#lerp(x21, x31, s), z0 = this.#lerp(y00, y10, v), z1 = this.#lerp(y01, y11, v);
return this.#lerp(z0, z1, t);
}
seed(inputSeed) {
const grad4 = this.#grad4, gradP = this.#gradP, perm = this.#perm, p = this.#p;
let seed = inputSeed;
if (seed > 0 && seed < 1) {
seed *= 65536;
}
seed = Math.floor(seed);
if (seed < 256) {
seed |= seed << 8;
}
const grad4Length = grad4.length;
for (let i = 0; i < 256; i++) {
const v = i & 1 ? p[i] ^ (seed & 255) : p[i] ^ ((seed >> 8) & 255);
perm[i] = perm[i + 256] = v;
gradP[i] = gradP[i + 256] = grad4[v % grad4Length];
}
}
#fade(t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
#lerp(a, b, t) {
return (1 - t) * a + t * b;
}
}
class PerlinNoiseGenerator extends NoiseFieldGenerator {
constructor(container) {
const perlinNoise = new PerlinNoise();
super(container, {
noise4d: (x, y, z, w) => perlinNoise.noise4d(x, y, z, w),
seed: seed => {
perlinNoise.seed(seed);
},
});
}
}
const perlinNoisePathName = "perlinNoise";
async function loadPerlinNoisePath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(perlinNoisePathName, container => {
return Promise.resolve(new PerlinNoiseGenerator(container));
});
});
}
class Poisson {
dimensions = 2;
enable = false;
radius = 0;
retries = 30;
steps = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "dimensions", data.dimensions);
loadProperty(this, "radius", data.radius);
loadProperty(this, "retries", data.retries);
}
}
class PoissonDiscPlugin {
id = "poisson";
async getPlugin(container) {
const { PoissonDiscPluginInstance } = await Promise.resolve().then(function () { return PoissonDiscPluginInstance$1; });
return new PoissonDiscPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let poissonOptions = options.poisson;
if (poissonOptions?.load === undefined) {
options.poisson = poissonOptions = new Poisson();
}
poissonOptions.load(source?.poisson);
}
needsPlugin(options) {
return options?.poisson?.enable ?? false;
}
}
async function loadPoissonDiscPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new PoissonDiscPlugin());
});
}
class PolygonMaskDrawStroke {
color = new OptionsColor();
opacity = 1;
width = 0.5;
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
load(data) {
if (isNull(data)) {
return;
}
this.color = OptionsColor.create(this.color, data.color);
if (isString(this.color.value)) {
this.opacity = stringToAlpha(this.#pluginManager, this.color.value) ?? this.opacity;
}
loadProperty(this, "opacity", data.opacity);
loadProperty(this, "width", data.width);
}
}
class PolygonMaskDraw {
enable = false;
stroke;
constructor(pluginManager) {
this.stroke = new PolygonMaskDrawStroke(pluginManager);
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
const stroke = data.stroke;
this.stroke.load(stroke);
}
}
var PolygonMaskInlineArrangement;
(function (PolygonMaskInlineArrangement) {
PolygonMaskInlineArrangement["equidistant"] = "equidistant";
PolygonMaskInlineArrangement["onePerPoint"] = "one-per-point";
PolygonMaskInlineArrangement["perPoint"] = "per-point";
PolygonMaskInlineArrangement["randomLength"] = "random-length";
PolygonMaskInlineArrangement["randomPoint"] = "random-point";
})(PolygonMaskInlineArrangement || (PolygonMaskInlineArrangement = {}));
class PolygonMaskInline {
arrangement;
constructor() {
this.arrangement = PolygonMaskInlineArrangement.onePerPoint;
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "arrangement", data.arrangement);
}
}
class PolygonMaskLocalSvg {
path = [];
size;
constructor() {
this.size = {
height: 0,
width: 0,
};
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "path", data.path);
if (data.size !== undefined) {
if (data.size.width !== undefined) {
this.size.width = data.size.width;
}
if (data.size.height !== undefined) {
this.size.height = data.size.height;
}
}
}
}
var PolygonMaskMoveType;
(function (PolygonMaskMoveType) {
PolygonMaskMoveType["path"] = "path";
PolygonMaskMoveType["radius"] = "radius";
})(PolygonMaskMoveType || (PolygonMaskMoveType = {}));
class PolygonMaskMove {
radius = 10;
type = PolygonMaskMoveType.path;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "radius", data.radius);
loadProperty(this, "type", data.type);
}
}
var PolygonMaskType;
(function (PolygonMaskType) {
PolygonMaskType["inline"] = "inline";
PolygonMaskType["inside"] = "inside";
PolygonMaskType["outside"] = "outside";
PolygonMaskType["none"] = "none";
})(PolygonMaskType || (PolygonMaskType = {}));
class PolygonMask {
data;
draw;
enable = false;
inline = new PolygonMaskInline();
move = new PolygonMaskMove();
position;
scale = 1;
type = PolygonMaskType.none;
url;
constructor(pluginManager) {
this.draw = new PolygonMaskDraw(pluginManager);
}
load(data) {
if (isNull(data)) {
return;
}
this.draw.load(data.draw);
this.inline.load(data.inline);
this.move.load(data.move);
loadProperty(this, "scale", data.scale);
loadProperty(this, "type", data.type);
if (data.enable !== undefined) {
this.enable = data.enable;
}
else {
this.enable = this.type !== PolygonMaskType.none;
}
loadProperty(this, "url", data.url);
if (data.data !== undefined) {
if (isString(data.data)) {
this.data = data.data;
}
else {
this.data = new PolygonMaskLocalSvg();
this.data.load(data.data);
}
}
if (data.position !== undefined) {
this.position = deepExtend({}, data.position);
}
}
}
class PolygonMaskPlugin {
id = "polygon-mask";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { PolygonMaskInstance } = await Promise.resolve().then(function () { return PolygonMaskInstance$1; });
return new PolygonMaskInstance(this.#pluginManager, container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let polygonOptions = options.polygon;
if (polygonOptions?.load === undefined) {
options.polygon = polygonOptions = new PolygonMask(this.#pluginManager);
}
polygonOptions.load(source?.polygon);
}
needsPlugin(options) {
return (options?.polygon?.enable ??
(options?.polygon?.type !== undefined && options.polygon.type !== PolygonMaskType.none));
}
}
async function loadPolygonMaskPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new PolygonMaskPlugin(e.pluginManager));
});
}
const defaultOptions$2 = {
sides: 6,
turnSteps: 20,
angle: 30,
};
class PolygonPathGenerator {
dirsList;
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.dirsList = [];
this.options = deepExtend({}, defaultOptions$2);
}
generate(p) {
const { sides, turnSteps } = this.options;
p.hexStep ??= 0;
p.hexDirection ??= sides === 6 ? ((getRandom() * 3) | 0) * 2 : (getRandom() * sides) | 0;
p.hexSpeed ??= p.velocity.length;
if (p.hexStep % turnSteps === 0) {
p.hexDirection = getRandom() > 0.5 ? (p.hexDirection + 1) % sides : (p.hexDirection + sides - 1) % sides;
}
p.velocity.x = 0;
p.velocity.y = 0;
p.hexStep++;
const direction = this.dirsList[p.hexDirection];
this.#res.x = direction.x * p.hexSpeed;
this.#res.y = direction.y * p.hexSpeed;
return this.#res;
}
init() {
const container = this.#container, sourceOptions = container.actualOptions.particles.move.path.options;
this.options.sides =
sourceOptions["sides"] > 0 ? sourceOptions["sides"] : this.options.sides;
this.options.angle = sourceOptions["angle"] ?? this.options.angle;
this.options.turnSteps =
sourceOptions["turnSteps"] >= 0 ? sourceOptions["turnSteps"] : this.options.turnSteps;
this.#createDirs();
}
reset(particle) {
delete particle.hexStep;
delete particle.hexDirection;
delete particle.hexSpeed;
}
update() {
}
#createDirs() {
const options = this.options;
this.dirsList = [];
for (let i = 0; i < 360; i += 360 / options.sides) {
const angle = options.angle + i;
this.dirsList.push(Vector.create(Math.cos((angle * Math.PI) / 180), Math.sin((angle * Math.PI) / 180)));
}
}
}
const polygonPathName = "polygonPathGenerator";
async function loadPolygonPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(polygonPathName, container => {
return Promise.resolve(new PolygonPathGenerator(container));
});
});
}
const minRandom = -1, maxRandom = 1;
class RandomPathGenerator {
#res;
constructor() {
this.#res = Vector.origin;
}
generate() {
this.#res.x = getRandomInRange(minRandom, maxRandom);
this.#res.y = getRandomInRange(minRandom, maxRandom);
return this.#res;
}
init() {
}
reset() {
}
update() {
}
}
const randomPathName = "randomPathGenerator";
async function loadRandomPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(randomPathName, () => {
return Promise.resolve(new RandomPathGenerator());
});
});
}
var ResponsiveMode;
(function (ResponsiveMode) {
ResponsiveMode["screen"] = "screen";
ResponsiveMode["window"] = "window";
ResponsiveMode["canvas"] = "canvas";
})(ResponsiveMode || (ResponsiveMode = {}));
class Responsive {
maxWidth = Infinity;
mode = ResponsiveMode.canvas;
options = {};
load(data) {
if (isNull(data)) {
return;
}
if (!isNull(data.maxWidth)) {
this.maxWidth = data.maxWidth;
}
if (!isNull(data.mode)) {
if (data.mode === ResponsiveMode.screen) {
this.mode = ResponsiveMode.screen;
}
else {
this.mode = ResponsiveMode.canvas;
}
}
if (!isNull(data.options)) {
this.options = deepExtend({}, data.options);
}
}
}
class ResponsivePlugin {
id = "responsive";
async getPlugin(container) {
const { ResponsivePluginInstance } = await Promise.resolve().then(function () { return ResponsivePluginInstance$1; });
return new ResponsivePluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
const setResponsive = (width, pxRatio, defaultOptions) => {
options.load(defaultOptions);
const logicalWidth = width / pxRatio, widthMap = {
[ResponsiveMode.screen]: screen.availWidth,
[ResponsiveMode.window]: innerWidth,
[ResponsiveMode.canvas]: logicalWidth,
}, responsiveOptions = options.responsive?.find(t => t.maxWidth > widthMap[t.mode]);
options.load(responsiveOptions?.options);
return responsiveOptions?.maxWidth;
};
options.setResponsive = setResponsive;
options.responsive ??= [];
if (source?.responsive !== undefined) {
for (const responsive of source.responsive) {
const optResponsive = new Responsive();
if (responsive) {
optResponsive.load(responsive);
}
options.responsive.push(optResponsive);
}
}
options.responsive.sort((a, b) => a.maxWidth - b.maxWidth);
}
needsPlugin(options) {
return !!options?.responsive?.length;
}
}
async function loadResponsivePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ResponsivePlugin());
});
}
const defaultParticleDist = 8, defaultMass = 1, defaultDrag = 0.05, defaultVelocityInherit = 5, defaultOscillationSpeed = 3, defaultOscillationDistance = 40, defaultAngle = 45, defaultPointCount = 30, minPointCount = 4, noPoint = 0, firstPoint = 1, secondPoint = 2, lineWidth = 1, randomTimeFactor = 100, degrees = 180, fixedDurationFrames = 50, fixedDuration = firstPoint / fixedDurationFrames, millisecondsToSeconds = 1000, minDelta = 1e-6, maxVelocityInherit = 800, noDistance = 0, half = 0.5;
function setRibbonBounds(particle, width, height) {
particle.ribbonBounds = Vector.create(width, height);
}
function getMass(particle) {
return particle.ribbonMass ?? defaultMass;
}
function getDrag(particle) {
return particle.ribbonDrag ?? defaultDrag;
}
function getVelocityInherit(particle) {
return particle.ribbonVelocityInherit ?? defaultVelocityInherit;
}
function getParticleDist(particle) {
return particle.ribbonParticleDist ?? defaultParticleDist;
}
function getOscillationSpeed(particle) {
return particle.ribbonOscillationSpeed ?? defaultOscillationSpeed;
}
function getOscillationDistance(particle) {
return particle.ribbonOscillationDistance ?? defaultOscillationDistance;
}
function getHead(particle) {
particle.ribbonHead ??= Vector.create(particle.position.x, particle.position.y);
return particle.ribbonHead;
}
function getPreviousHead(particle) {
particle.ribbonPreviousHead ??= Vector.create(particle.position.x, particle.position.y);
return particle.ribbonPreviousHead;
}
function createRibbonState(particle) {
const shapeData = particle.shapeData, count = Math.max(minPointCount, Math.round(getRangeValue(shapeData?.count ?? defaultPointCount))), particleDist = getRangeValue(shapeData?.particleDist ?? defaultParticleDist), mass = getRangeValue(shapeData?.mass ?? defaultMass), drag = getRangeValue(shapeData?.drag ?? defaultDrag), velocityInherit = getRangeValue(shapeData?.velocityInherit ?? defaultVelocityInherit), oscillationSpeed = getRangeValue(shapeData?.oscillationSpeed ?? defaultOscillationSpeed), oscillationDistance = getRangeValue(shapeData?.oscillationDistance ?? defaultOscillationDistance), angle = getRangeValue(shapeData?.angle ?? defaultAngle), angleRad = (Math.PI / degrees) * angle, head = Vector.create(particle.position.x, particle.position.y), bounds = particle.ribbonBounds;
if (!bounds) {
return;
}
particle.ribbonParticleDist = particleDist;
particle.ribbonMass = mass;
particle.ribbonDrag = drag;
particle.ribbonVelocityInherit = velocityInherit;
particle.ribbonOscillationSpeed = oscillationSpeed;
particle.ribbonOscillationDistance = oscillationDistance;
particle.ribbonTime = getRandom() * randomTimeFactor;
particle.ribbonOffsets = Vector.create(Math.cos(angleRad), Math.sin(angleRad));
particle.ribbonHead = head;
particle.ribbonPreviousHead = Vector.create(head.x, head.y);
particle.ribbonPreviousPosition = Vector.create(particle.position.x, particle.position.y);
const trailAngle = particle.velocity.angle + Math.PI, noiseScale = particleDist * half;
particle.ribbonPoints = new Array(count);
for (let i = noPoint; i < count; i++) {
const offset = i * particleDist, perpNoise = (getRandom() - half) * noiseScale, perpAngle = trailAngle + Math.PI * half;
particle.ribbonPoints[i] = {
force: Vector.origin,
position: Vector.create(head.x + Math.cos(trailAngle) * offset + Math.cos(perpAngle) * perpNoise, head.y + Math.sin(trailAngle) * offset + Math.sin(perpAngle) * perpNoise),
velocity: Vector.origin,
};
}
}
function resetRibbonState(particle) {
const points = particle.ribbonPoints, bounds = particle.ribbonBounds, width = bounds?.x ?? noDistance, height = bounds?.y ?? noDistance, position = Vector.create(getRandom() * width, -getRandom() * height);
if (!bounds) {
return;
}
if (!points?.length) {
createRibbonState(particle);
return;
}
particle.position.x = position.x;
particle.position.y = position.y;
particle.ribbonHead = Vector.create(position.x, position.y);
particle.ribbonPreviousHead = Vector.create(position.x, position.y);
particle.ribbonPreviousPosition = Vector.create(position.x, position.y);
particle.ribbonTime = getRandom() * randomTimeFactor;
for (let i = noPoint; i < points.length; i++) {
const point = points[i];
if (!point) {
continue;
}
point.position = Vector.create(position.x, position.y - i * getParticleDist(particle));
point.force = Vector.origin;
point.velocity = Vector.origin;
}
}
function addPointForce(particle, index, force) {
const point = particle.ribbonPoints?.[index];
if (!point) {
return;
}
point.force.addTo(force);
}
function integratePoint(particle, index, dt) {
const point = particle.ribbonPoints?.[index];
if (!point) {
return;
}
const mass = getMass(particle), drag = getDrag(particle), totalForce = Vector.create(point.force.x, point.force.y), speed = point.velocity.length, dragVelocity = Vector.create(point.velocity.x, point.velocity.y);
dragVelocity.multTo(drag * mass * speed);
totalForce.subFrom(dragVelocity);
const acceleration = totalForce.div(mass);
point.position.addTo(point.velocity.mult(dt));
point.velocity.addTo(acceleration.mult(dt));
point.force = Vector.origin;
}
function updateRibbon(data) {
const { particle, delta } = data, points = particle.ribbonPoints, offsets = particle.ribbonOffsets, bounds = particle.ribbonBounds;
if (!points?.length || !offsets || !bounds) {
return;
}
const dt = Math.min(fixedDuration, Math.max(delta.value / millisecondsToSeconds, minDelta)), head = getHead(particle), previousHead = getPreviousHead(particle), velocityInherit = getVelocityInherit(particle), followsParticleMotion = particle.options.move.enable;
particle.ribbonTime = (particle.ribbonTime ?? noPoint) + dt * getOscillationSpeed(particle);
if (followsParticleMotion) {
head.x =
particle.position.x +
(Math.cos(particle.ribbonTime) * getOscillationDistance(particle)) / getOscillationSpeed(particle);
head.y = particle.position.y;
}
else {
head.x += Math.cos(particle.ribbonTime) * getOscillationDistance(particle) * dt;
}
if (particle.justWarped) {
const trailAngle = particle.velocity.angle + Math.PI, dist = getParticleDist(particle), firstPointObj = points[noPoint];
if (firstPointObj) {
firstPointObj.position = Vector.create(head.x, head.y);
firstPointObj.velocity = Vector.origin;
firstPointObj.force = Vector.origin;
}
for (let i = firstPoint; i < points.length; i++) {
const point = points[i];
if (!point) {
continue;
}
const offset = i * dist;
point.position = Vector.create(head.x + Math.cos(trailAngle) * offset, head.y + Math.sin(trailAngle) * offset);
point.velocity = Vector.origin;
point.force = Vector.origin;
}
}
const particleDist = getParticleDist(particle);
if (!Number.isFinite(head.x) || !Number.isFinite(head.y)) {
resetRibbonState(particle);
return;
}
const first = points[noPoint];
if (!first) {
return;
}
first.position = Vector.create(head.x, head.y);
const dX = previousHead.x - head.x, dY = previousHead.y - head.y, movement = Math.sqrt(dX * dX + dY * dY), velocityFactor = Math.min((movement / dt) * velocityInherit, maxVelocityInherit);
particle.ribbonPreviousHead = Vector.create(head.x, head.y);
particle.ribbonPreviousPosition = Vector.create(particle.position.x, particle.position.y);
for (let i = firstPoint; i < points.length; i++) {
const point = points[i], previousPoint = points[i - firstPoint];
if (!point || !previousPoint) {
continue;
}
const direction = previousPoint.position.sub(point.position).copy();
direction.normalize();
direction.multTo(velocityFactor);
addPointForce(particle, i, direction);
}
for (let i = firstPoint; i < points.length; i++) {
integratePoint(particle, i, dt);
}
for (let i = firstPoint; i < points.length; i++) {
const point = points[i], previousPoint = points[i - firstPoint];
if (!point || !previousPoint) {
continue;
}
const normalized = point.position.sub(previousPoint.position).copy();
normalized.normalize();
normalized.multTo(particleDist);
point.position = previousPoint.position.add(normalized);
if (!Number.isFinite(point.position.x) ||
!Number.isFinite(point.position.y) ||
!Number.isFinite(point.velocity.x) ||
!Number.isFinite(point.velocity.y)) {
resetRibbonState(particle);
return;
}
}
}
function getSegmentSide(x1, y1, x2, y2, x3, y3) {
return (x1 - x2) * (y3 - y2) - (y1 - y2) * (x3 - x2);
}
function getMidpoint(a, b) {
return Vector.create((a.x + b.x) * half, (a.y + b.y) * half);
}
function drawPolygonSegment(context, drawData) {
context.beginPath();
context.moveTo(drawData.a.x, drawData.a.y);
context.lineTo(drawData.b.x, drawData.b.y);
context.lineTo(drawData.c.x, drawData.c.y);
if (drawData.d) {
context.lineTo(drawData.d.x, drawData.d.y);
}
context.closePath();
if (drawData.stroke) {
context.stroke();
}
if (drawData.fill) {
context.fill();
}
}
function resolveBackFill(hasStroke, frontFill, frontStroke, shapeData, particle, hdr, peakNits, mode) {
let backFill = hasStroke ? frontStroke : frontFill;
if (shapeData?.backColor) {
backFill = shapeData.backColor;
}
else if (shapeData?.darken?.enable) {
const frontHsl = getHslFromAnimation(particle.fillColor);
if (frontHsl) {
const altered = alterHsl(frontHsl, exports.AlterType.darken, getRangeValue(shapeData.darken.value));
backFill = getStyleFromHsl(altered, hdr, undefined, peakNits, mode);
}
}
else if (shapeData?.enlighten?.enable) {
const frontHsl = getHslFromAnimation(particle.fillColor);
if (frontHsl) {
const altered = alterHsl(frontHsl, exports.AlterType.enlighten, getRangeValue(shapeData.enlighten.value));
backFill = getStyleFromHsl(altered, hdr, undefined, peakNits, mode);
}
}
return backFill;
}
function drawEdgeSegments(context, currentBase, nextBase, currentSide, nextSide, isFirst, isLast, hasFill, hasStroke) {
if (isFirst) {
drawPolygonSegment(context, {
a: currentBase,
b: nextBase,
c: getMidpoint(nextBase, nextSide),
fill: hasFill,
stroke: hasStroke,
});
drawPolygonSegment(context, {
a: nextSide,
b: currentSide,
c: getMidpoint(nextBase, nextSide),
fill: hasFill,
stroke: hasStroke,
});
}
else if (isLast) {
drawPolygonSegment(context, {
a: currentBase,
b: nextBase,
c: getMidpoint(currentBase, currentSide),
fill: hasFill,
stroke: hasStroke,
});
drawPolygonSegment(context, {
a: nextSide,
b: currentSide,
c: getMidpoint(currentBase, currentSide),
fill: hasFill,
stroke: hasStroke,
});
}
else {
drawPolygonSegment(context, {
a: currentBase,
b: nextBase,
c: nextSide,
d: currentSide,
fill: hasFill,
stroke: hasStroke,
});
}
}
function drawRibbon(data, hdr, peakNits, mode) {
const { context, particle, radius } = data, points = particle.ribbonPoints, offsets = particle.ribbonOffsets, bounds = particle.ribbonBounds, hasFill = data.fill, hasStroke = data.stroke;
if (!points || !offsets || !bounds || points.length < minPointCount || (!hasFill && !hasStroke)) {
return;
}
const drawOffsets = offsets.mult(radius), center = particle.position, frontFill = typeof context.fillStyle === "string" ? context.fillStyle : "", frontStroke = typeof context.strokeStyle === "string" ? context.strokeStyle : "", shapeData = particle.shapeData, backFill = resolveBackFill(hasStroke, frontFill, frontStroke, shapeData, particle, hdr, peakNits, mode);
context.lineWidth = lineWidth;
context.lineJoin = "round";
context.lineCap = "round";
for (let i = noPoint; i < points.length - firstPoint; i++) {
const current = points[i], next = points[i + firstPoint];
if (!current || !next) {
continue;
}
const currentOffset = current.position.add(drawOffsets), nextOffset = next.position.add(drawOffsets), currentBase = current.position.sub(center), nextBase = next.position.sub(center), currentSide = currentOffset.sub(center), nextSide = nextOffset.sub(center), minY = Math.min(current.position.y, next.position.y, currentOffset.y, nextOffset.y), maxY = Math.max(current.position.y, next.position.y, currentOffset.y, nextOffset.y), isFront = getSegmentSide(current.position.x, current.position.y, next.position.x, next.position.y, nextOffset.x, nextOffset.y) < noDistance, segmentStyle = isFront ? frontFill : backFill;
if (minY > bounds.y + drawOffsets.length || maxY < -drawOffsets.length) {
continue;
}
context.fillStyle = segmentStyle;
if (hasStroke) {
context.strokeStyle = segmentStyle;
}
if (i === noPoint) {
drawEdgeSegments(context, currentBase, nextBase, currentSide, nextSide, true, false, hasFill, hasStroke);
}
else if (i === points.length - secondPoint) {
drawEdgeSegments(context, currentBase, nextBase, currentSide, nextSide, false, true, hasFill, hasStroke);
}
else {
drawEdgeSegments(context, currentBase, nextBase, currentSide, nextSide, false, false, hasFill, hasStroke);
}
}
context.fillStyle = frontFill;
if (hasStroke) {
context.strokeStyle = frontStroke;
}
context.beginPath();
}
const defaultSides$1 = 12, defaultRibbonCount = 30, minRibbonCount = 4, sidesMultiplier = 2, minRadius$2 = 0, insideMargin = 0;
function prepareRibbonParticle(particle, width, height) {
setRibbonBounds(particle, width, height);
if (!particle.ribbonPoints?.length) {
createRibbonState(particle);
}
}
function getRibbonCount(particle) {
return Math.max(minRibbonCount, Math.round(getRangeValue(particle.shapeData?.count ?? defaultRibbonCount)));
}
function isInsideByDirection(data, minX, maxX, minY, maxY, margin) {
const { canvasSize, direction } = data;
if (direction === exports.OutModeDirection.bottom) {
return minY <= canvasSize.height + margin;
}
if (direction === exports.OutModeDirection.left) {
return maxX >= -margin;
}
if (direction === exports.OutModeDirection.right) {
return minX <= canvasSize.width + margin;
}
if (direction === exports.OutModeDirection.top) {
return maxY >= -margin;
}
return maxX >= -margin && maxY >= -margin && minX <= canvasSize.width + margin && minY <= canvasSize.height + margin;
}
class RibbonDrawer {
#container;
constructor(container) {
this.#container = container;
}
draw(data) {
const bounds = this.#container.canvas.size;
prepareRibbonParticle(data.particle, bounds.width, bounds.height);
if (data.particle.ribbonPoints && data.particle.ribbonPoints.length !== getRibbonCount(data.particle)) {
createRibbonState(data.particle);
}
updateRibbon(data);
drawRibbon(data, this.#container.hdr, this.#container.peakNits, this.#container.hdrMode);
}
getSidesCount(particle) {
const pointsLen = particle.ribbonPoints?.length;
if (!pointsLen) {
return defaultSides$1;
}
return Math.max(defaultSides$1, pointsLen * sidesMultiplier);
}
isInsideCanvas(data) {
const { outMode, particle, radius } = data, points = particle.ribbonPoints, offsets = particle.ribbonOffsets, position = particle.position, strictBounds = outMode === exports.OutMode.destroy || outMode === exports.OutMode.out, scaledOffsetLen = (offsets?.length ?? minRadius$2) * radius, headRadius = Math.max(radius, scaledOffsetLen);
if (!strictBounds) {
return isInsideByDirection(data, position.x - headRadius, position.x + headRadius, position.y - headRadius, position.y + headRadius, insideMargin);
}
if (!points?.length || !offsets) {
return isInsideByDirection(data, position.x - headRadius, position.x + headRadius, position.y - headRadius, position.y + headRadius, insideMargin);
}
let minX = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
for (const point of points) {
const baseX = point.position.x, baseY = point.position.y, sideX = baseX + offsets.x * radius, sideY = baseY + offsets.y * radius;
minX = Math.min(minX, baseX, sideX);
maxX = Math.max(maxX, baseX, sideX);
minY = Math.min(minY, baseY, sideY);
maxY = Math.max(maxY, baseY, sideY);
}
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
return false;
}
const oscAmp = particle.ribbonOscillationSpeed && particle.ribbonOscillationDistance
? particle.ribbonOscillationDistance / particle.ribbonOscillationSpeed
: minRadius$2, margin = Math.max(headRadius, particle.ribbonParticleDist ?? minRadius$2) + oscAmp;
return isInsideByDirection(data, minX, maxX, minY, maxY, margin);
}
loadShape(particle) {
if (!particle.shapeData) {
return;
}
const count = getRibbonCount(particle);
if (!particle.ribbonPoints?.length || particle.ribbonPoints.length !== count) {
createRibbonState(particle);
}
}
particleDestroy(particle) {
delete particle.ribbonPoints;
delete particle.ribbonHead;
delete particle.ribbonPreviousHead;
delete particle.ribbonPreviousPosition;
delete particle.ribbonTime;
delete particle.ribbonOffsets;
delete particle.ribbonParticleDist;
delete particle.ribbonMass;
delete particle.ribbonDrag;
delete particle.ribbonVelocityInherit;
delete particle.ribbonOscillationSpeed;
delete particle.ribbonOscillationDistance;
}
particleInit(container, particle) {
prepareRibbonParticle(particle, container.canvas.size.width, container.canvas.size.height);
}
}
async function loadRibbonShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["ribbon"], (container) => Promise.resolve(new RibbonDrawer(container)));
});
}
const defaultRotation = 0;
function polygon(sides, radius, rot = defaultRotation) {
const step = doublePI / sides, path = [];
for (let i = 0; i < sides; i++) {
path.push({ x: Math.cos(i * step + rot) * radius, y: Math.sin(i * step + rot) * radius });
}
return path;
}
function roundedPath(context, path, radius) {
const index1 = 0, index2 = 1, increment = 1;
let p1 = path[index1], p2 = path[index2];
if (!p1 || !p2) {
return;
}
const len = path.length;
context.moveTo((p1.x + p2.x) * half$2, (p1.y + p2.y) * half$2);
for (let i = 1; i <= len; i++) {
p1 = p2;
p2 = path[(i + increment) % len];
if (!p1 || !p2) {
continue;
}
context.arcTo(p1.x, p1.y, (p1.x + p2.x) * half$2, (p1.y + p2.y) * half$2, radius);
}
}
const defaultSides = 5, defaultRadius$1 = 5;
class RoundedPolygonDrawer {
draw(data) {
const { context, particle, radius } = data;
roundedPath(context, polygon(particle.sides, radius), particle.borderRadius ?? defaultRadius$1);
}
getSidesCount(particle) {
const roundedPolygon = particle.shapeData;
return Math.round(getRangeValue(roundedPolygon?.sides ?? defaultSides));
}
particleInit(container, particle) {
const shapeData = particle.shapeData;
particle.borderRadius = Math.round(getRangeValue(shapeData?.radius ?? defaultSides)) * container.retina.pixelRatio;
}
}
async function loadRoundedPolygonShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["rounded-polygon"], () => Promise.resolve(new RoundedPolygonDrawer()));
});
}
function drawRoundedRect(ctx, fixedRadius, fixedDiameter, borderRadius) {
const info = {
x: -fixedRadius,
y: -fixedRadius,
height: fixedDiameter,
width: fixedDiameter,
}, radius = {
topLeft: borderRadius,
topRight: borderRadius,
bottomLeft: borderRadius,
bottomRight: borderRadius,
}, { x, y, width, height } = info, r = x + width, b = y + height;
ctx.moveTo(x + radius.topLeft, y);
ctx.lineTo(r - radius.topRight, y);
ctx.quadraticCurveTo(r, y, r, y + radius.topRight);
ctx.lineTo(r, y + height - radius.bottomRight);
ctx.quadraticCurveTo(r, b, r - radius.bottomRight, b);
ctx.lineTo(x + radius.bottomLeft, b);
ctx.quadraticCurveTo(x, b, x, b - radius.bottomLeft);
ctx.lineTo(x, y + radius.topLeft);
ctx.quadraticCurveTo(x, y, x + radius.topLeft, y);
}
const defaultRadius = 5;
class RoundedRectDrawer {
draw(data) {
const { context, particle, radius } = data, fixedRadius = radius * Math.SQRT1_2, fixedDiameter = fixedRadius * double, borderRadius = particle.borderRadius ?? defaultRadius;
if ("roundRect" in context) {
context.roundRect(-fixedRadius, -fixedRadius, fixedDiameter, fixedDiameter, borderRadius);
}
else {
drawRoundedRect(context, fixedRadius, fixedDiameter, borderRadius);
}
}
particleInit(container, particle) {
const shapeData = particle.shapeData;
particle.borderRadius = getRangeValue(shapeData?.radius ?? defaultRadius) * container.retina.pixelRatio;
}
}
async function loadRoundedRectShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["rounded-rect"], () => Promise.resolve(new RoundedRectDrawer()));
});
}
function createSVGPaths(data) {
const doc = safeDocument();
return data.map(d => {
const element = doc.createElementNS("http://www.w3.org/2000/svg", "path");
element.setAttribute("d", d);
return {
element,
length: element.getTotalLength(),
};
});
}
function loadSVGFromString(svgText) {
const parser = new DOMParser(), doc = parser.parseFromString(svgText, "image/svg+xml"), svg = doc.querySelector("svg");
if (!svg) {
return { paths: [], size: { width: 0, height: 0 } };
}
const pathElements = svg.querySelectorAll("path"), paths = [];
for (const node of pathElements) {
if (!(node instanceof SVGPathElement)) {
continue;
}
paths.push({
element: node,
length: node.getTotalLength(),
});
}
const size = {
width: Number.parseFloat(svg.getAttribute("width") ?? "0"),
height: Number.parseFloat(svg.getAttribute("height") ?? "0"),
};
return {
paths,
size,
};
}
var SVGPathDirection;
(function (SVGPathDirection) {
SVGPathDirection[SVGPathDirection["normal"] = 0] = "normal";
SVGPathDirection[SVGPathDirection["reverse"] = 1] = "reverse";
})(SVGPathDirection || (SVGPathDirection = {}));
const minStep = 0, minIndex = 0, minWidth$2 = 0, minScale = 1;
class SVGPathGenerator {
#container;
#offset;
#paths;
#res;
#reverse;
#scale;
#size;
#width;
constructor(container) {
this.#container = container;
this.#paths = [];
this.#reverse = false;
this.#size = { width: 0, height: 0 };
this.#scale = 1;
this.#offset = { x: 0, y: 0, mode: exports.PixelMode.percent };
this.#width = 0;
this.#res = Vector.origin;
}
generate(particle, delta) {
const container = this.#container, pxRatio = container.retina.pixelRatio;
particle.svgDirection ??= getRandom() > half$2 ? SVGPathDirection.normal : SVGPathDirection.reverse;
particle.svgPathIndex ??= Math.floor(getRandom() * this.#paths.length);
particle.svgSpeed ??= particle.velocity.mult(particle.retina.moveSpeed * half$2).length;
particle.svgStep ??= randomInRangeValue({ min: 0, max: this.#paths[particle.svgPathIndex].length }) * pxRatio;
particle.svgOffset ??= {
width: randomInRangeValue({ min: -this.#width * half$2, max: this.#width * half$2 }) * pxRatio,
height: randomInRangeValue({ min: -this.#width * half$2, max: this.#width * half$2 }) * pxRatio,
};
particle.svgInitialPosition ??= particle.position.copy();
particle.velocity.x = 0;
particle.velocity.y = 0;
if (particle.svgDirection === SVGPathDirection.normal) {
particle.svgStep += particle.svgSpeed * delta.factor;
}
else {
particle.svgStep -= particle.svgSpeed * delta.factor;
}
let path = this.#paths[particle.svgPathIndex];
const pathLength = path.length, indexOffset = 1;
if (particle.svgStep >= pathLength) {
particle.svgPathIndex = particle.svgPathIndex + indexOffset;
if (particle.svgPathIndex >= this.#paths.length) {
if (this.#reverse) {
particle.svgPathIndex = this.#paths.length - indexOffset;
particle.svgDirection = SVGPathDirection.reverse;
}
else {
particle.svgPathIndex = 0;
particle.svgStep = 0;
}
}
}
else if (particle.svgStep <= minStep) {
particle.svgPathIndex = particle.svgPathIndex - indexOffset;
if (particle.svgPathIndex < minIndex) {
if (this.#reverse) {
particle.svgPathIndex = 0;
particle.svgDirection = SVGPathDirection.normal;
}
else {
particle.svgPathIndex = this.#paths.length - indexOffset;
path = this.#paths[particle.svgPathIndex];
particle.svgStep = path.length;
}
}
}
path = this.#paths[particle.svgPathIndex];
const pathElement = path.element, pos = pathElement.getPointAtLength(particle.svgStep), canvasSize = this.#container.canvas.size, offset = getPosition(this.#offset, canvasSize), scale = this.#scale * pxRatio;
particle.position.x =
(pos.x - this.#size.width * half$2) * scale + particle.svgInitialPosition.x + offset.x + particle.svgOffset.width;
particle.position.y =
(pos.y - this.#size.height * half$2) * scale + particle.svgInitialPosition.y + offset.y + particle.svgOffset.height;
this.#res.x = 0;
this.#res.y = 0;
return this.#res;
}
init() {
const options = this.#container.actualOptions.particles.move.path.options, position = options.position ?? this.#offset;
this.#reverse = options.reverse ?? this.#reverse;
this.#scale = options.scale ?? minScale;
this.#offset.x = position.x;
this.#offset.y = position.y;
this.#offset.mode = position.mode;
this.#width = options.width ?? minWidth$2;
if (options.url && !options.path) {
const url = options.url;
void (async () => {
const response = await fetch(url), data = await response.text(), { paths, size } = loadSVGFromString(data);
this.#paths = paths;
this.#size.width = size.width;
this.#size.height = size.height;
})();
}
else if (options.path) {
const path = options.path;
this.#paths = createSVGPaths(options.path.data);
this.#size.height = path.size.height;
this.#size.width = path.size.width;
}
}
reset() {
}
update() {
}
}
const svgPathName = "svgPathGenerator";
async function loadSVGPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(svgPathName, container => {
return Promise.resolve(new SVGPathGenerator(container));
});
});
}
const defaultShadowBlur = 0, defaultShadowOffsetValue = 0;
class ShadowDrawer {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
drawAfter(data) {
const { context } = data;
context.restore();
}
drawBefore(data) {
const { particle, context } = data, container = this.#container, shadowParticle = particle, shadowColor = shadowParticle.shadowColor, shadowOffset = shadowParticle.shadowOffset;
if (!shadowColor) {
return;
}
context.save();
context.shadowBlur = shadowParticle.shadowBlur ?? defaultShadowBlur;
context.shadowColor = getStyleFromRgb(shadowColor, container.hdr, undefined, container.peakNits, container.hdrMode);
context.shadowOffsetX = shadowOffset?.x ?? defaultShadowOffsetValue;
context.shadowOffsetY = shadowOffset?.y ?? defaultShadowOffsetValue;
}
particleInit(_container, particle) {
const effectData = particle.effectData, shadowColor = OptionsColor.create(new OptionsColor(), effectData?.color);
particle.shadowColor = rangeColorToRgb(this.#pluginManager, shadowColor);
particle.shadowBlur = effectData?.blur ?? defaultShadowBlur;
particle.shadowOffset = effectData?.offset ?? originPoint;
}
}
async function loadShadowEffect(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addEffect("shadow", container => {
return Promise.resolve(new ShadowDrawer(e.pluginManager, container));
});
});
}
class SimplexNoiseGenerator extends NoiseFieldGenerator {
constructor(container) {
const simplex = new SimplexNoise();
super(container, {
noise4d: (x, y, z, w) => simplex.noise4d.noise(x, y, z, w),
seed: seed => {
simplex.noise4d.seed(seed);
},
});
}
}
const simplexNoisePathName = "simplexNoise";
async function loadSimplexNoisePath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(simplexNoisePathName, container => {
return Promise.resolve(new SimplexNoiseGenerator(container));
});
});
}
const mouseDownEvent = "pointerdown", touchStartEvent = "touchstart";
class SoundsAudio {
loop = false;
source = "";
load(data) {
if (isNull(data)) {
return;
}
if (isObject(data)) {
loadProperty(this, "loop", data.loop);
if (data.source !== undefined) {
this.source = data.source;
}
}
else {
this.source = data;
}
}
}
class SoundsNote {
duration = 500;
value = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "duration", data.duration);
loadProperty(this, "value", data.value);
}
}
class SoundsMelody {
loop = false;
melodies = [];
notes = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "loop", data.loop);
if (data.melodies !== undefined) {
this.melodies = data.melodies.map(s => {
const tmp = new SoundsMelody();
tmp.load(s);
return tmp;
});
}
if (data.notes !== undefined) {
this.notes = data.notes.map(s => {
const tmp = new SoundsNote();
tmp.load(s);
return tmp;
});
}
}
}
class SoundsEvent {
audio;
event = [];
filter;
melodies;
notes = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "event", data.event);
if (data.audio !== undefined) {
if (isArray(data.audio)) {
this.audio = data.audio.map(s => {
const tmp = new SoundsAudio();
tmp.load(s);
return tmp;
});
}
else {
this.audio = new SoundsAudio();
this.audio.load(data.audio);
}
}
if (data.notes !== undefined) {
this.notes = data.notes.map(t => {
const tmp = new SoundsNote();
tmp.load(t);
return tmp;
});
}
if (data.melodies !== undefined) {
this.melodies = data.melodies.map(t => {
const tmp = new SoundsMelody();
tmp.load(t);
return tmp;
});
}
if (data.filter) {
if (isString(data.filter)) {
const filterFunc = globalThis[data.filter];
if (isFunction(filterFunc)) {
this.filter = filterFunc;
}
}
else {
this.filter = data.filter;
}
}
}
}
class SoundsIcon {
height = 24;
path;
style = "";
svg;
width = 24;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "path", data.path);
loadProperty(this, "svg", data.svg);
loadProperty(this, "width", data.width);
loadProperty(this, "height", data.height);
}
}
class SoundsIcons {
enable;
mute;
unmute;
volumeDown;
volumeUp;
constructor() {
this.mute = new SoundsIcon();
this.unmute = new SoundsIcon();
this.volumeDown = new SoundsIcon();
this.volumeUp = new SoundsIcon();
this.enable = false;
this.mute.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M19.707,5.293c-0.391-0.391-1.023-0.391-1.414,0l-1.551,1.551c-0.345-0.688-0.987-1.02-1.604-1.02c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C10.357,8.561,8.904,9,8,9c-1.654,0-3,1.346-3,3v2c0,1.237,0.754,2.302,1.826,2.76l-1.533,1.533c-0.391,0.391-0.391,1.023,0,1.414C5.488,19.902,5.744,20,6,20s0.512-0.098,0.707-0.293l2.527-2.527c0.697,0.174,1.416,0.455,1.875,0.762l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C16.035,20.176,17,19.495,17,18V9.414l2.707-2.707C20.098,6.316,20.098,5.684,19.707,5.293z M14.891,7.941c0.038-0.025,0.073-0.046,0.104-0.062C14.998,7.914,15,7.954,15,8v1.293l-2,2V9.202L14.891,7.941z M7,12c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v2.439l-2.83,2.83C8.757,15.046,8.356,15,8,15c-0.552,0-1-0.448-1-1V12z M10.301,15.406L12,13.707v2.439C11.519,15.859,10.925,15.604,10.301,15.406z M14.994,18.12c-0.03-0.016-0.065-0.036-0.104-0.062L13,16.798v-4.091l2-2V18C15,18.046,14.998,18.086,14.994,18.12z"/>
</g>
</svg>`;
this.unmute.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M17.138,5.824c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C12.357,8.561,10.904,9,10,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C18.035,20.176,19,19.495,19,18V8C19,6.505,18.035,5.824,17.138,5.824z M14,16.146C12.907,15.495,11.211,15,10,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146V16.146z M17,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L15,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C16.998,7.914,17,7.954,17,8V18z"/>
</g>
</svg>`;
this.volumeDown.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M15.138,5.824c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C10.357,8.561,8.904,9,8,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C16.035,20.176,17,19.495,17,18V8C17,6.505,16.035,5.824,15.138,5.824z M8,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v6.293C10.907,15.495,9.211,15,8,15z M15,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L13,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C14.998,7.914,15,7.954,15,8V18z"/>
<path fill="#fff" d="M18.292,10.294c-0.39,0.391-0.39,1.023,0.002,1.414c0.345,0.345,0.535,0.803,0.535,1.291c0,0.489-0.19,0.948-0.536,1.294c-0.391,0.39-0.391,1.023,0,1.414C18.488,15.902,18.744,16,19,16s0.512-0.098,0.707-0.293c0.724-0.723,1.122-1.685,1.122-2.708s-0.398-1.984-1.123-2.707C19.317,9.903,18.683,9.901,18.292,10.294z"/>
</g>
</svg>`;
this.volumeUp.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M16.706,10.292c-0.389-0.389-1.023-0.391-1.414,0.002c-0.39,0.391-0.39,1.023,0.002,1.414c0.345,0.345,0.535,0.803,0.535,1.291c0,0.489-0.19,0.948-0.536,1.294c-0.391,0.39-0.391,1.023,0,1.414C15.488,15.902,15.744,16,16,16s0.512-0.098,0.707-0.293c0.724-0.723,1.122-1.685,1.122-2.708S17.431,11.015,16.706,10.292z"/>
<path fill="#fff" d="M18.706,8.292c-0.391-0.389-1.023-0.39-1.414,0.002c-0.39,0.391-0.39,1.024,0.002,1.414c0.879,0.877,1.363,2.044,1.364,3.287c0.001,1.246-0.484,2.417-1.365,3.298c-0.391,0.391-0.391,1.023,0,1.414C17.488,17.902,17.744,18,18,18s0.512-0.098,0.707-0.293c1.259-1.259,1.952-2.933,1.951-4.713C20.657,11.217,19.964,9.547,18.706,8.292z"/>
<path fill="#fff" d="M20.706,6.292c-0.391-0.389-1.023-0.39-1.414,0.002c-0.39,0.391-0.39,1.024,0.002,1.414c1.412,1.409,2.191,3.285,2.192,5.284c0.002,2.002-0.777,3.885-2.193,5.301c-0.391,0.391-0.391,1.023,0,1.414C19.488,19.902,19.744,20,20,20s0.512-0.098,0.707-0.293c1.794-1.794,2.781-4.18,2.779-6.717C23.485,10.457,22.497,8.078,20.706,6.292z"/>
<path fill="#fff" d="M12.138,5.824c-0.449,0-0.905,0.152-1.356,0.453L8.109,8.059C7.357,8.561,5.904,9,5,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C13.035,20.176,14,19.495,14,18V8C14,6.505,13.035,5.824,12.138,5.824z M5,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v6.293C7.907,15.495,6.211,15,5,15z M12,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L10,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C11.998,7.914,12,7.954,12,8V18z"/>
</g>
</svg>`;
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
this.mute.load(data.mute);
this.unmute.load(data.unmute);
this.volumeDown.load(data.volumeDown);
this.volumeUp.load(data.volumeUp);
}
}
class SoundsVolume {
max = 100;
min = 0;
step = 10;
value = 100;
load(data) {
if (isNull(data)) {
return;
}
if (isObject(data)) {
loadProperty(this, "max", data.max);
loadProperty(this, "min", data.min);
loadProperty(this, "step", data.step);
if (data.value !== undefined) {
this.value = data.value;
}
}
else {
this.value = data;
}
}
}
class Sounds {
autoPlay = true;
enable = false;
events = [];
icons = new SoundsIcons();
volume = new SoundsVolume();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "autoPlay", data.autoPlay);
loadProperty(this, "enable", data.enable);
if (data.events !== undefined) {
this.events = data.events.map(t => {
const event = new SoundsEvent();
event.load(t);
return event;
});
}
this.icons.load(data.icons);
if (data.volume !== undefined) {
this.volume.load(data.volume);
}
}
}
const notes = new Map();
notes.set("C", [16.35, 32.7, 65.41, 130.81, 261.63, 523.25, 1046.5, 2093.0, 4186.01]);
notes.set("Db", [17.32, 34.65, 69.3, 138.59, 277.18, 554.37, 1108.73, 2217.46, 4434.92]);
notes.set("D", [18.35, 36.71, 73.42, 146.83, 293.66, 587.33, 1174.66, 2349.32, 4698.63]);
notes.set("Eb", [19.45, 38.89, 77.78, 155.56, 311.13, 622.25, 1244.51, 2489.02, 4978.03]);
notes.set("E", [20.6, 41.2, 82.41, 164.81, 329.63, 659.25, 1318.51, 2637.02, 5274.04]);
notes.set("F", [21.83, 43.65, 87.31, 174.61, 349.23, 698.46, 1396.91, 2793.83, 5587.65]);
notes.set("Gb", [23.12, 46.25, 92.5, 185.0, 369.99, 739.99, 1479.98, 2959.96, 5919.91]);
notes.set("G", [24.5, 49.0, 98.0, 196.0, 392.0, 783.99, 1567.98, 3135.96, 6271.93]);
notes.set("Ab", [25.96, 51.91, 103.83, 207.65, 415.3, 830.61, 1661.22, 3322.44, 6644.88]);
notes.set("A", [27.5, 55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0]);
notes.set("Bb", [29.14, 58.27, 116.54, 233.08, 466.16, 932.33, 1864.66, 3729.31, 7458.62]);
notes.set("B", [30.87, 61.74, 123.47, 246.94, 493.88, 987.77, 1975.53, 3951.07, 7902.13]);
notes.set("pause", [0]);
function getNoteFrequency(note) {
const regex = /(([A-G]b?)(\d))|pause/i, result = regex.exec(note), groupKey = 2, defaultMatchKey = 0, innerGroupKey = 3;
if (!result?.length) {
return;
}
const noteKey = result[groupKey] ?? result[defaultMatchKey], noteItem = notes.get(noteKey);
if (!noteItem) {
return;
}
return noteItem[parseInt(result[innerGroupKey] ?? "0")];
}
let muted = true;
const isWindowMuted = () => {
return muted;
};
const unmuteWindow = () => {
muted = false;
};
const generalFirstClickHandler = () => {
removeEventListener(mouseDownEvent, generalFirstClickHandler);
removeEventListener(touchStartEvent, generalFirstClickHandler);
unmuteWindow();
};
class SoundsPlugin {
id = "sounds";
#engine;
constructor(engine) {
this.#engine = engine;
const listenerOptions = {
capture: true,
once: true,
};
addEventListener(mouseDownEvent, generalFirstClickHandler, listenerOptions);
addEventListener(touchStartEvent, generalFirstClickHandler, listenerOptions);
}
async getPlugin(container) {
const { SoundsPluginInstance } = await Promise.resolve().then(function () { return SoundsPluginInstance$1; });
return new SoundsPluginInstance(container, this.#engine);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let soundsOptions = options.sounds;
if (soundsOptions?.load === undefined) {
options.sounds = soundsOptions = new Sounds();
}
soundsOptions.load(source?.sounds);
}
needsPlugin(options) {
return options?.sounds?.enable ?? false;
}
}
async function loadSoundsPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new SoundsPlugin(e));
});
}
var SpiralDirection;
(function (SpiralDirection) {
SpiralDirection[SpiralDirection["positive"] = 1] = "positive";
SpiralDirection[SpiralDirection["negative"] = -1] = "negative";
})(SpiralDirection || (SpiralDirection = {}));
const minRadius$1 = 0, speedFactor = 0.01, defaultOptions$1 = {
maxRadius: { min: 10, max: 50 },
angularSpeed: { min: 0.01, max: 0.04 },
radialSpeed: { min: 0.1, max: 0.3 },
};
class SpiralPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = deepExtend({}, defaultOptions$1);
}
generate(particle, delta) {
const options = this.options;
particle.spiral ??= {
angle: getRandom() * doublePI,
radius: minRadius$1,
direction: SpiralDirection.positive,
maxRadius: getRangeValue(options.maxRadius),
angularSpeed: getRangeValue(options.angularSpeed) * speedFactor,
radialSpeed: getRangeValue(options.radialSpeed) * speedFactor,
};
const spiral = particle.spiral;
spiral.angle += spiral.angularSpeed * delta.factor;
spiral.radius += spiral.radialSpeed * spiral.direction * delta.factor;
if (spiral.radius > spiral.maxRadius && spiral.direction === SpiralDirection.positive) {
spiral.radius = spiral.maxRadius;
spiral.direction = SpiralDirection.negative;
}
else if (spiral.radius < minRadius$1 && spiral.direction === SpiralDirection.negative) {
spiral.radius = minRadius$1;
spiral.direction = SpiralDirection.positive;
}
const offsetX = Math.cos(spiral.angle) * spiral.radius, offsetY = Math.sin(spiral.angle) * spiral.radius;
particle.position.x += offsetX;
particle.position.y += offsetY;
this.#res.x = 0;
this.#res.y = 0;
return this.#res;
}
init() {
const sourceOptions = this.#container.actualOptions.particles.move.path.options;
this.options.maxRadius = sourceOptions["maxRadius"] ?? this.options.maxRadius;
this.options.angularSpeed = sourceOptions["angularSpeed"] ?? this.options.angularSpeed;
this.options.radialSpeed = sourceOptions["radialSpeed"] ?? this.options.radialSpeed;
}
reset(particle) {
delete particle.spiral;
}
update() {
}
}
const spiralPathName = "spiralPathGenerator";
async function loadSpiralPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(spiralPathName, container => {
return Promise.resolve(new SpiralPathGenerator(container));
});
});
}
function drawSpiral(data) {
const { context, particle, radius } = data;
if (particle.spiralInnerRadius === undefined ||
particle.spiralLineSpacing === undefined ||
particle.spiralWidthFactor === undefined) {
return;
}
const realWidth = (radius - particle.spiralInnerRadius) / particle.spiralLineSpacing, widthFactor = 10;
for (let i = 0; i < realWidth * widthFactor; i++) {
const angle = i / widthFactor, factor = particle.spiralInnerRadius + particle.spiralLineSpacing * angle, pos = {
x: factor * Math.cos(angle),
y: factor * Math.sin(angle),
};
context.lineTo(pos.x, pos.y);
}
}
const defaultInnerRadius = 1, defaultLineSpacing = 1, defaultWidthFactor = 10;
class SpiralDrawer {
draw(data) {
drawSpiral(data);
}
particleInit(container, particle) {
const pixelRatio = container.retina.pixelRatio, shapeData = particle.shapeData;
particle.spiralInnerRadius = getRangeValue(shapeData?.innerRadius ?? defaultInnerRadius) * pixelRatio;
particle.spiralLineSpacing = getRangeValue(shapeData?.lineSpacing ?? defaultLineSpacing) * pixelRatio;
particle.spiralWidthFactor = getRangeValue(shapeData?.widthFactor ?? defaultWidthFactor);
}
}
async function loadSpiralShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["spiral"], () => Promise.resolve(new SpiralDrawer()));
});
}
const defaultExponent = 4, defaultSteps = 64;
function sign(v) {
return v < none ? -identity$3 : identity$3;
}
function drawSquircle(data) {
const { context, particle, radius } = data;
if (particle.squircleExponent === undefined || particle.squircleSteps === undefined) {
return;
}
const steps = particle.squircleSteps, step = doublePI / steps, exponent = double / particle.squircleExponent;
context.save();
context.scale(radius, radius);
context.beginPath();
for (let i = 0; i <= steps; i++) {
const t = i * step, cos = Math.cos(t), sin = Math.sin(t), x = sign(cos) * Math.pow(Math.abs(cos), exponent), y = sign(sin) * Math.pow(Math.abs(sin), exponent);
if (i) {
context.lineTo(x, y);
}
else {
context.moveTo(x, y);
}
}
context.closePath();
context.restore();
}
class SquircleDrawer {
draw(data) {
drawSquircle(data);
}
particleInit(_container, particle) {
const shapeData = particle.shapeData;
particle.squircleExponent = getRangeValue(shapeData?.exponent ?? defaultExponent);
particle.squircleSteps = getRangeValue(shapeData?.steps ?? defaultSteps);
}
}
async function loadSquircleShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["squircle"], () => Promise.resolve(new SquircleDrawer()));
});
}
var ThemeMode;
(function (ThemeMode) {
ThemeMode["any"] = "any";
ThemeMode["dark"] = "dark";
ThemeMode["light"] = "light";
})(ThemeMode || (ThemeMode = {}));
class ThemeDefault {
auto = false;
mode = ThemeMode.any;
value = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "auto", data.auto);
loadProperty(this, "mode", data.mode);
loadProperty(this, "value", data.value);
}
}
class Theme {
default = new ThemeDefault();
name = "";
options;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "name", data.name);
this.default.load(data.default);
if (data.options !== undefined) {
this.options = deepExtend({}, data.options);
}
}
}
class ThemesPlugin {
id = "theme";
async getPlugin(container) {
const { ThemesPluginInstance } = await Promise.resolve().then(function () { return ThemesPluginInstance$1; });
return new ThemesPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
const findDefaultTheme = (mode) => {
if (!options.themes) {
return;
}
return (options.themes.find(theme => theme.default.value && theme.default.mode === mode) ??
options.themes.find(theme => theme.default.value && theme.default.mode === ThemeMode.any));
}, setTheme = (name) => {
if (!options.themes) {
return;
}
if (name) {
const chosenTheme = options.themes.find(theme => theme.name === name);
if (chosenTheme) {
options.load(chosenTheme.options);
}
}
else {
const mediaMatch = safeMatchMedia("(prefers-color-scheme: dark)"), clientDarkMode = mediaMatch?.matches, defaultTheme = findDefaultTheme(clientDarkMode ? ThemeMode.dark : ThemeMode.light);
if (defaultTheme) {
options.load(defaultTheme.options);
}
}
};
options.findDefaultTheme = findDefaultTheme;
options.setTheme = setTheme;
options.themes ??= [];
if (source?.themes !== undefined) {
for (const theme of source.themes) {
if (!theme) {
continue;
}
const existingTheme = options.themes.find(t => t.name === theme.name);
if (existingTheme) {
existingTheme.load(theme);
}
else {
const optTheme = new Theme();
optTheme.load(theme);
options.themes.push(optTheme);
}
}
}
options.defaultThemes.dark = findDefaultTheme(ThemeMode.dark)?.name;
options.defaultThemes.light = findDefaultTheme(ThemeMode.light)?.name;
}
needsPlugin(options) {
return !!options?.themes?.length;
}
}
async function loadThemesPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ThemesPlugin());
});
}
const minTrailLength = 3, trailLengthOffset = 1, minWidth$1 = -1, firstIndex = 0, defaultLength = 10, loopTrailLengthOffset = 2, loopTrailLengthMinIndex = 0, defaultWidth = 0, minBound = 0;
const defaultTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};
class TrailDrawer {
#container;
constructor(container) {
this.#container = container;
}
drawAfter(data) {
const { context, drawPosition, drawRadius, drawScale, particle, transformData } = data, container = this.#container, diameter = drawRadius * double, pxRatio = container.retina.pixelRatio, trail = particle.trail;
if (!trail || !particle.trailLength) {
return;
}
const currentPos = drawPosition, pathLength = particle.trailLength * drawScale + drawRadius;
trail.push({
break: particle.justWarped,
color: context.fillStyle || context.strokeStyle,
position: {
x: currentPos.x,
y: currentPos.y,
},
transformData,
});
if (trail.length < minTrailLength) {
return;
}
const pathLengthFloor = Math.floor(pathLength);
if (trail.length > pathLengthFloor) {
trail.splice(firstIndex, trail.length - pathLengthFloor);
}
const trailLength = Math.min(trail.length, pathLength);
context.save();
context.lineCap = "butt";
context.lineJoin = "round";
for (let i = trailLength - loopTrailLengthOffset; i > loopTrailLengthMinIndex; i--) {
const previousStep = trail[i + trailLengthOffset], step = trail[i], nextStep = trail[i - trailLengthOffset];
if (!previousStep || !step || !nextStep) {
continue;
}
const previousPosition = previousStep.position, position = step.position, nextPosition = nextStep.position, stepTransformData = particle.trailTransform ? (step.transformData ?? defaultTransform) : defaultTransform;
if (step.break || previousStep.break || nextStep.break) {
continue;
}
context.setTransform(stepTransformData.a, stepTransformData.b, stepTransformData.c, stepTransformData.d, position.x, position.y);
const startX = (previousPosition.x + position.x) * half$2 - position.x, startY = (previousPosition.y + position.y) * half$2 - position.y, endX = (position.x + nextPosition.x) * half$2 - position.x, endY = (position.y + nextPosition.y) * half$2 - position.y;
context.beginPath();
context.moveTo(startX, startY);
context.quadraticCurveTo(originPoint.x, originPoint.y, endX, endY);
const width = Math.max((i / trailLength) * diameter, pxRatio, (particle.trailMinWidth ?? minWidth$1) * drawScale), oldAlpha = context.globalAlpha;
context.globalAlpha = particle.trailFade ? i / trailLength : defaultAlpha$1;
context.lineWidth = particle.trailMaxWidth ? Math.min(width, particle.trailMaxWidth * drawScale) : width;
context.strokeStyle = step.color;
context.stroke();
context.globalAlpha = oldAlpha;
}
context.restore();
}
isInsideCanvas(data) {
const { canvasSize, direction, outMode, particle, radius } = data, position = particle.position, trail = particle.trail;
let minX = position.x - radius, maxX = position.x + radius, minY = position.y - radius, maxY = position.y + radius;
if (trail?.length) {
const widthPadding = Math.max(radius, particle.trailMaxWidth ?? defaultWidth, particle.trailMinWidth ?? defaultWidth);
for (const step of trail) {
minX = Math.min(minX, step.position.x - widthPadding);
maxX = Math.max(maxX, step.position.x + widthPadding);
minY = Math.min(minY, step.position.y - widthPadding);
maxY = Math.max(maxY, step.position.y + widthPadding);
}
}
const strictBounds = outMode === exports.OutMode.destroy;
if (direction === exports.OutModeDirection.bottom) {
return minY <= canvasSize.height;
}
if (direction === exports.OutModeDirection.left) {
return maxX >= minBound;
}
if (direction === exports.OutModeDirection.right) {
return minX <= canvasSize.width;
}
if (direction === exports.OutModeDirection.top) {
return maxY >= minBound;
}
if (!strictBounds) {
return (position.x + radius >= minBound &&
position.y + radius >= minBound &&
position.x - radius <= canvasSize.width &&
position.y - radius <= canvasSize.height);
}
return maxX >= minBound && maxY >= minBound && minX <= canvasSize.width && minY <= canvasSize.height;
}
particleInit(container, particle) {
particle.trail = [];
const effectData = particle.effectData;
particle.trailFade = effectData?.fade ?? true;
particle.trailLength = getRangeValue(effectData?.length ?? defaultLength) * container.retina.pixelRatio;
particle.trailMaxWidth = effectData?.maxWidth
? getRangeValue(effectData.maxWidth) * container.retina.pixelRatio
: undefined;
particle.trailMinWidth = effectData?.minWidth
? getRangeValue(effectData.minWidth) * container.retina.pixelRatio
: undefined;
particle.trailTransform = effectData?.transform ?? false;
}
}
async function loadTrailEffect(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addEffect("trail", container => {
return Promise.resolve(new TrailDrawer(container));
});
});
}
class TrailFill {
color;
image;
load(data) {
if (isNull(data)) {
return;
}
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "image", data.image);
}
}
class Trail {
enable = false;
fill = new TrailFill();
length = 10;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
if (data.fill !== undefined) {
this.fill.load(data.fill);
}
loadProperty(this, "length", data.length);
}
}
class TrailPlugin {
id = "trail";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { TrailPluginInstance } = await Promise.resolve().then(function () { return TrailPluginInstance$1; });
return new TrailPluginInstance(this.#pluginManager, container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin()) {
return;
}
let trailOptions = options.trail;
if (!trailOptions?.load) {
options.trail = trailOptions = new Trail();
}
trailOptions.load(source?.trail);
}
needsPlugin() {
return true;
}
}
async function loadTrailPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new TrailPlugin(e.pluginManager));
});
}
const angularFrequencyFactor = 0.5, halfPI = Math.PI * half$2;
const defaultOptions = {
waveHeight: { min: 0, max: 3 },
waveLength: { min: 0, max: 5 },
};
class ZigZagPathGenerator {
options;
#container;
#res;
constructor(container) {
this.#container = container;
this.#res = Vector.origin;
this.options = deepExtend({}, defaultOptions);
}
generate(particle, delta) {
const options = this.options;
particle.zigzag ??= {
counter: getRandom(),
waveHeight: getRangeValue(options.waveHeight),
waveLength: getRangeValue(options.waveLength),
};
const angularFrequency = (angularFrequencyFactor / particle.zigzag.waveLength) * delta.factor;
particle.zigzag.counter += angularFrequency;
const zigzagAngle = particle.zigzag.waveHeight * Math.sin(particle.zigzag.counter);
particle.position.x += zigzagAngle * Math.cos(particle.velocity.angle + halfPI);
particle.position.y += zigzagAngle * Math.sin(particle.velocity.angle + halfPI);
this.#res.x = 0;
this.#res.y = 0;
return this.#res;
}
init() {
const sourceOptions = this.#container.actualOptions.particles.move.path.options;
this.options.waveLength = sourceOptions["waveLength"] ?? this.options.waveLength;
this.options.waveHeight = sourceOptions["waveHeight"] ?? this.options.waveHeight;
}
reset() {
}
update() {
}
}
const zigZagPathName = "zigZagPathGenerator";
async function loadZigZagPath(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureBaseMoverLoaded(e);
e.pluginManager.addPathGenerator?.(zigZagPathName, container => {
return Promise.resolve(new ZigZagPathGenerator(container));
});
});
}
const minZoom = 0.1;
const maxZoom = 5;
const zoomInFactor = 1.05;
const zoomOutFactor = 0.95;
const zoomGestureFactor = 0.25;
const touchPointsCount = 2;
const touchPointIndexFirst = 0;
const touchPointIndexSecond = 1;
const touchCenterDivisor = 2;
const initialTouchDistance = 0;
class Zoom {
enable = false;
max = maxZoom;
min = minZoom;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "min", data.min);
loadProperty(this, "max", data.max);
}
}
class ZoomPlugin {
id = "zoom";
async getPlugin(container) {
const { ZoomPluginInstance } = await Promise.resolve().then(function () { return ZoomPluginInstance$1; });
return new ZoomPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin()) {
return;
}
let zoomOptions = options.zoom;
if (!zoomOptions) {
zoomOptions = new Zoom();
options.zoom = zoomOptions;
}
zoomOptions.load(source?.zoom);
}
needsPlugin() {
return true;
}
}
async function loadZoomPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new ZoomPlugin());
});
}
async function loadAll(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const loadInteractionsForAll = async (e) => {
await loadFull(e);
await Promise.all([
loadExternalCannonInteraction(e),
loadExternalParticleInteraction(e),
loadExternalPopInteraction(e),
loadLightInteraction(e),
loadParticlesRepulseInteraction(e),
loadInfectionPlugin(e),
loadEmittersShapeCanvas(e),
loadEmittersShapePath(e),
loadEmittersShapePolygon(e),
loadBranchesPath(e),
loadBrownianPath(e),
loadCurlNoisePath(e),
loadCurvesPath(e),
loadFractalNoisePath(e),
loadGridPath(e),
loadLevyPath(e),
loadPerlinNoisePath(e),
loadPolygonPath(e),
loadRandomPath(e),
loadSVGPath(e),
loadSpiralPath(e),
loadZigZagPath(e),
loadSimplexNoisePath(e),
]);
};
await Promise.all([
loadInteractionsForAll(e),
loadHsvColorPlugin(e),
loadHwbColorPlugin(e),
loadLabColorPlugin(e),
loadLchColorPlugin(e),
loadOklabColorPlugin(e),
loadOklchColorPlugin(e),
loadNamedColorPlugin(e),
loadEasingBackPlugin(e),
loadEasingBouncePlugin(e),
loadEasingCircPlugin(e),
loadEasingCubicPlugin(e),
loadEasingElasticPlugin(e),
loadEasingExpoPlugin(e),
loadEasingGaussianPlugin(e),
loadEasingLinearPlugin(e),
loadEasingQuartPlugin(e),
loadEasingQuintPlugin(e),
loadEasingSigmoidPlugin(e),
loadEasingSinePlugin(e),
loadEasingSmoothstepPlugin(e),
loadBackgroundMaskPlugin(e),
loadCanvasMaskPlugin(e),
loadManualParticlesPlugin(e),
loadMotionPlugin(e),
loadPoissonDiscPlugin(e),
loadPolygonMaskPlugin(e),
loadResponsivePlugin(e),
loadSoundsPlugin(e),
loadThemesPlugin(e),
loadTrailPlugin(e),
loadZoomPlugin(e),
loadExportImagePlugin(e),
loadExportJSONPlugin(e),
loadExportVideoPlugin(e),
loadGradientUpdater(e),
loadOrbitUpdater(e),
loadBubbleEffect(e),
loadFilterEffect(e),
loadParticlesEffect(e),
loadShadowEffect(e),
loadTrailEffect(e),
loadArrowShape(e),
loadCardsShape(e),
loadCogShape(e),
loadGifShape(e),
loadHeartShape(e),
loadInfinityShape(e),
loadMatrixShape(e),
loadPathShape(e),
loadRibbonShape(e),
loadRoundedPolygonShape(e),
loadRoundedRectShape(e),
loadSpiralShape(e),
loadSquircleShape(e),
]);
});
}
const globalObject = globalThis;
globalObject.loadAll = loadAll;
globalObject.tsParticles = tsParticles;
const fColorIndex = 0, sColorIndex = 1;
function setTransformValue(factor, newFactor, key) {
const newValue = newFactor[key];
if (newValue !== undefined) {
factor[key] = (factor[key] ?? defaultTransformValue) * newValue;
}
}
class RenderManager {
#backgroundElement;
#backgroundWarnings;
#canvasClearPlugins;
#canvasManager;
#colorPlugins;
#container;
#context;
#contextSettings;
#drawParticlePlugins;
#drawParticlesCleanupPlugins;
#drawParticlesSetupPlugins;
#layers;
#pluginManager;
#postDrawUpdaters;
#preDrawUpdaters;
#reusableColorStyles = {};
#reusablePluginColors = [undefined, undefined];
#reusableTransform = {};
constructor(pluginManager, container, canvasManager) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#canvasManager = canvasManager;
this.#context = null;
this.#backgroundElement = null;
this.#backgroundWarnings = new Set();
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesCleanupPlugins = [];
this.#drawParticlesSetupPlugins = [];
this.#layers = {
0: [],
1: [],
2: [],
3: [],
4: [],
5: [],
6: [],
7: [],
};
}
get settings() {
return this.#contextSettings;
}
canvasClear() {
if (!this.#container.actualOptions.clear) {
return;
}
this.draw(ctx => {
ctx.clearRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
clear() {
for (const plugin of this.#canvasClearPlugins) {
if (plugin.canvasClear?.() ?? false) {
return;
}
}
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
for (const plugin of this.#getLayerPlugins(layer)) {
if (plugin.canvasClear?.() ?? false) {
return;
}
}
}
}
this.canvasClear();
}
destroy() {
this.stop();
this.#backgroundElement = null;
this.#backgroundWarnings.clear();
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesCleanupPlugins = [];
this.#drawParticlesSetupPlugins = [];
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
this.#layers[layer] = [];
}
}
}
draw(cb) {
const ctx = this.#context;
if (!ctx) {
return;
}
return cb(ctx);
}
drawParticle(particle, delta) {
if (particle.spawning || particle.destroyed) {
return;
}
const radius = particle.getRadius();
if (radius <= minimumSize) {
return;
}
const pfColor = particle.getFillColor(), psColor = particle.getStrokeColor();
let [fColor, sColor] = this.#getPluginParticleColors(particle);
fColor ??= pfColor;
sColor ??= psColor;
if (!fColor && !sColor) {
return;
}
const container = this.#container, zIndexOptions = particle.options.zIndex, zIndexFactor = zIndexFactorOffset - particle.zIndexFactor, { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(), transform = this.#reusableTransform, colorStyles = this.#reusableColorStyles, fill = fColor
? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity, container.peakNits, container.hdrMode)
: undefined, stroke = sColor
? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity, container.peakNits, container.hdrMode)
: fill;
transform.a = transform.b = transform.c = transform.d = undefined;
colorStyles.fill = fill;
colorStyles.stroke = stroke;
this.draw((context) => {
for (const plugin of this.#drawParticlesSetupPlugins) {
plugin.drawParticleSetup?.(context, particle, delta);
}
this.#applyPreDrawUpdaters(context, particle, radius, opacity, colorStyles, transform);
this.#drawParticle({
container,
context,
particle,
delta,
colorStyles,
radius: radius * zIndexFactor ** zIndexOptions.sizeRate,
opacity: opacity,
transform,
});
this.#applyPostDrawUpdaters(particle);
for (const plugin of this.#drawParticlesCleanupPlugins) {
plugin.drawParticleCleanup?.(context, particle, delta);
}
});
}
drawParticlePlugins(particle, delta) {
this.draw(ctx => {
for (const plugin of this.#drawParticlePlugins) {
this.#drawParticlePlugin(ctx, plugin, particle, delta);
}
});
}
drawParticles(delta) {
const { particles, actualOptions } = this.#container;
this.clear();
particles.update(delta);
this.draw(ctx => {
const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
if (this.#backgroundElement) {
try {
ctx.drawImage(this.#backgroundElement, originPoint.x, originPoint.y, width, height);
}
catch {
this.#warnOnce("background-element-draw-error", "Error drawing background element onto canvas");
}
}
const background = actualOptions.background;
if (background.draw) {
try {
background.draw(ctx, delta);
}
catch {
this.#warnOnce("background-draw-error", "Error in background.draw callback");
}
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.BackgroundMask)) {
plugin.canvasPaint?.();
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.CanvasSetup)) {
plugin.drawSettingsSetup?.(ctx, delta);
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.PluginContent)) {
plugin.draw?.(ctx, delta);
}
particles.drawParticles(delta);
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.CanvasCleanup)) {
plugin.clearDraw?.(ctx, delta);
plugin.drawSettingsCleanup?.(ctx, delta);
}
});
}
init() {
this.initUpdaters();
this.initPlugins();
this.#resolveBackgroundElement();
this.paint();
}
initPlugins() {
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesSetupPlugins = [];
this.#drawParticlesCleanupPlugins = [];
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
this.#layers[layer] = [];
}
}
for (const plugin of this.#container.plugins) {
if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
this.#colorPlugins.push(plugin);
}
if (plugin.drawParticle) {
this.#drawParticlePlugins.push(plugin);
}
if (plugin.drawParticleSetup) {
this.#drawParticlesSetupPlugins.push(plugin);
}
if (plugin.drawParticleCleanup) {
this.#drawParticlesCleanupPlugins.push(plugin);
}
if (plugin.canvasClear) {
this.#canvasClearPlugins.push(plugin);
}
if (plugin.canvasPaint) {
this.#getLayerPlugins(exports.DrawLayer.BackgroundMask).push(plugin);
}
if (plugin.drawSettingsSetup) {
this.#getLayerPlugins(exports.DrawLayer.CanvasSetup).push(plugin);
}
if (plugin.draw) {
this.#getLayerPlugins(exports.DrawLayer.PluginContent).push(plugin);
}
if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
this.#getLayerPlugins(exports.DrawLayer.CanvasCleanup).push(plugin);
}
}
}
initUpdaters() {
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
for (const updater of this.#container.particleUpdaters) {
if (updater.afterDraw) {
this.#postDrawUpdaters.push(updater);
}
if (updater.getColorStyles ?? updater.getTransformValues ?? updater.beforeDraw) {
this.#preDrawUpdaters.push(updater);
}
}
}
paint() {
let handled = false;
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.BackgroundMask)) {
handled = plugin.canvasPaint?.() ?? false;
if (handled) {
break;
}
}
if (handled) {
return;
}
this.paintBase();
}
paintBase(baseColor) {
this.draw(ctx => {
ctx.fillStyle = baseColor ?? "rgba(0,0,0,0)";
ctx.fillRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
paintImage(image, opacity) {
this.draw(ctx => {
if (!image) {
return;
}
const prevAlpha = ctx.globalAlpha;
ctx.globalAlpha = opacity;
ctx.drawImage(image, originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
ctx.globalAlpha = prevAlpha;
});
}
setContext(context) {
this.#context = context;
if (this.#context) {
this.#context.globalCompositeOperation = defaultCompositeValue;
}
}
setContextSettings(settings) {
this.#contextSettings = settings;
}
stop() {
this.draw(ctx => {
ctx.clearRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
#applyPostDrawUpdaters(particle) {
for (const updater of this.#postDrawUpdaters) {
updater.afterDraw?.(particle);
}
}
#applyPreDrawUpdaters(ctx, particle, radius, zOpacity, colorStyles, transform) {
for (const updater of this.#preDrawUpdaters) {
if (updater.getColorStyles) {
const { fill, stroke } = updater.getColorStyles(particle, ctx, radius, zOpacity);
if (fill) {
colorStyles.fill = fill;
}
if (stroke) {
colorStyles.stroke = stroke;
}
}
if (updater.getTransformValues) {
const updaterTransform = updater.getTransformValues(particle);
for (const key in updaterTransform) {
setTransformValue(transform, updaterTransform, key);
}
}
updater.beforeDraw?.(particle);
}
}
#drawAfterEffect(drawer, data) {
if (!drawer?.drawAfter) {
return;
}
const { particle } = data;
if (!particle.effect) {
return;
}
drawer.drawAfter(data);
}
#drawBeforeEffect(drawer, data) {
if (!drawer?.drawBefore) {
return;
}
const { particle } = data;
if (!particle.effect) {
return;
}
drawer.drawBefore(data);
}
#drawParticle(data) {
const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, { effectDrawers, shapeDrawers } = container, pos = particle.getPosition(), transformData = particle.getTransformData(transform), drawScale = defaultZoom, drawPosition = {
x: pos.x,
y: pos.y,
};
context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
if (colorStyles.fill) {
context.fillStyle = colorStyles.fill;
}
const fillEnabled = !!particle.fillEnabled, strokeWidth = particle.strokeWidth ?? minStrokeWidth;
context.lineWidth = strokeWidth;
if (colorStyles.stroke) {
context.strokeStyle = colorStyles.stroke;
}
const drawData = {
context,
particle,
radius,
drawRadius: radius * drawScale,
opacity,
delta,
pixelRatio: container.retina.pixelRatio,
fill: fillEnabled,
stroke: strokeWidth > minStrokeWidth,
transformData,
position: { ...pos },
drawPosition,
drawScale,
};
for (const plugin of container.plugins) {
plugin.drawParticleTransform?.(drawData);
}
const effect = particle.effect ? effectDrawers.get(particle.effect) : undefined, shape = particle.shape ? shapeDrawers.get(particle.shape) : undefined;
this.#drawBeforeEffect(effect, drawData);
this.#drawShapeBeforeDraw(shape, drawData);
this.#drawShape(shape, drawData);
this.#drawShapeAfterDraw(shape, drawData);
this.#drawAfterEffect(effect, drawData);
context.resetTransform();
}
#drawParticlePlugin(context, plugin, particle, delta) {
if (!plugin.drawParticle) {
return;
}
plugin.drawParticle(context, particle, delta);
}
#drawShape(drawer, data) {
if (!drawer) {
return;
}
const { context, fill, particle, stroke } = data;
if (!particle.shape) {
return;
}
context.beginPath();
drawer.draw(data);
if (particle.shapeClose) {
context.closePath();
}
if (fill) {
context.fill();
}
if (stroke) {
context.stroke();
}
}
#drawShapeAfterDraw(drawer, data) {
if (!drawer?.afterDraw) {
return;
}
const { particle } = data;
if (!particle.shape) {
return;
}
drawer.afterDraw(data);
}
#drawShapeBeforeDraw(drawer, data) {
if (!drawer?.beforeDraw) {
return;
}
const { particle } = data;
if (!particle.shape) {
return;
}
drawer.beforeDraw(data);
}
#getLayerPlugins(layer) {
return this.#layers[layer];
}
#getPluginParticleColors(particle) {
let fColor, sColor;
for (const plugin of this.#colorPlugins) {
if (!fColor && plugin.particleFillColor) {
fColor = rangeColorToHsl(this.#pluginManager, plugin.particleFillColor(particle));
}
if (!sColor && plugin.particleStrokeColor) {
sColor = rangeColorToHsl(this.#pluginManager, plugin.particleStrokeColor(particle));
}
if (fColor && sColor) {
break;
}
}
this.#reusablePluginColors[fColorIndex] = fColor;
this.#reusablePluginColors[sColorIndex] = sColor;
return this.#reusablePluginColors;
}
#resolveBackgroundElement() {
const background = this.#container.actualOptions.background;
this.#backgroundElement = null;
if (!background.element) {
return;
}
if (typeof background.element === "string") {
if (typeof document !== "undefined") {
const node = document.querySelector(background.element);
if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
(typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
(typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
this.#backgroundElement = node;
}
else if (node) {
this.#warnOnce("background-element-not-supported", `Background element "${background.element}" is not a supported drawable element (canvas, video, or img)`);
}
else {
this.#warnOnce("background-element-not-found", `Background element selector "${background.element}" not found`);
}
}
}
else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
(typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
(typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
(typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
this.#backgroundElement = background.element;
}
}
#warnOnce(key, message) {
if (!this.#backgroundWarnings.has(key)) {
this.#backgroundWarnings.add(key);
getLogger().warning(message);
}
}
}
const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
const transferredCanvas = transferredCanvases.get(canvas);
if (transferredCanvas) {
return transferredCanvas;
}
if (typeof canvas.transferControlToOffscreen !== "function") {
throw new TypeError("OffscreenCanvas is required but not supported by this browser");
}
try {
const offscreenCanvas = canvas.transferControlToOffscreen();
transferredCanvases.set(canvas, offscreenCanvas);
return offscreenCanvas;
}
catch {
throw new TypeError("OffscreenCanvas transfer failed");
}
}, isHtmlCanvasElement = (canvas) => {
return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
};
function setStyle(canvas, style, important = false) {
if (!style) {
return;
}
const element = canvas, elementStyle = element.style, keys = new Set();
for (let i = 0; i < elementStyle.length; i++) {
const key = elementStyle.item(i);
if (!key) {
continue;
}
keys.add(key);
}
for (let i = 0; i < style.length; i++) {
const key = style.item(i);
if (!key) {
continue;
}
keys.add(key);
}
for (const key of keys) {
const value = style.getPropertyValue(key);
if (value) {
elementStyle.setProperty(key, value, important ? "important" : "");
}
else {
elementStyle.removeProperty(key);
}
}
}
class CanvasManager {
domElement;
render;
renderCanvas;
size;
zoom = defaultZoom;
#container;
#generated;
#hdrMediaListeners;
#mutationObserver;
#originalStyle;
#pluginManager;
#pointerEvents;
#resizePlugins;
#standardSize;
#zoomCenter;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.render = new RenderManager(pluginManager, container, this);
this.#standardSize = {
height: 0,
width: 0,
};
const pxRatio = container.retina.pixelRatio, stdSize = this.#standardSize;
this.size = {
height: stdSize.height * pxRatio,
width: stdSize.width * pxRatio,
};
this.#generated = false;
this.#resizePlugins = [];
this.#pointerEvents = "none";
}
get #fullScreen() {
return this.#container.actualOptions.fullScreen.enable;
}
destroy() {
this.stop();
if (this.#generated) {
const element = this.domElement;
element?.remove();
this.domElement = undefined;
this.renderCanvas = undefined;
}
else {
this.#resetOriginalStyle();
}
this.render.destroy();
this.#resizePlugins = [];
}
getZoomCenter() {
const pxRatio = this.#container.retina.pixelRatio, { width, height } = this.size;
if (this.#zoomCenter) {
return this.#zoomCenter;
}
return {
x: (width * half$2) / pxRatio,
y: (height * half$2) / pxRatio,
};
}
init() {
this.#safeMutationObserver(obs => {
obs.disconnect();
});
this.#mutationObserver = safeMutationObserver(records => {
for (const record of records) {
if (record.type === "attributes" && record.attributeName === "style") {
this.#repairStyle();
}
}
});
this.resize();
this.#initStyle();
this.#safeMutationObserver(obs => {
const element = this.domElement;
if (!element || !(element instanceof Node)) {
return;
}
obs.observe(element, { attributes: true });
});
this.initPlugins();
this.#initContext();
this.initBackground();
this.#initHdrListeners();
this.render.init();
}
initBackground() {
const container = this.#container, options = container.actualOptions, background = options.background, element = this.domElement;
if (!element) {
return;
}
const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
if (color) {
const hdrOptions = container.actualOptions.hdr;
elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
}
else {
elementStyle.backgroundColor = "";
}
elementStyle.backgroundImage = background.image || "";
elementStyle.backgroundPosition = background.position || "";
elementStyle.backgroundRepeat = background.repeat || "";
elementStyle.backgroundSize = background.size || "";
}
initPlugins() {
this.#resizePlugins = [];
for (const plugin of this.#container.plugins) {
if (plugin.resize) {
this.#resizePlugins.push(plugin);
}
}
}
loadCanvas(canvas) {
if (this.#generated && this.domElement) {
this.domElement.remove();
}
const domCanvas = isHtmlCanvasElement(canvas) ? canvas : undefined;
this.domElement = domCanvas;
this.#generated = domCanvas ? domCanvas.dataset[generatedAttribute] === "true" : false;
this.renderCanvas = domCanvas ? getTransferredCanvas(domCanvas) : canvas;
const domElement = this.domElement;
if (domElement) {
domElement.ariaHidden = "true";
this.#originalStyle = cloneStyle(domElement.style);
}
const standardSize = this.#standardSize, renderCanvas = this.renderCanvas;
if (domElement) {
standardSize.height = domElement.offsetHeight;
standardSize.width = domElement.offsetWidth;
}
else {
standardSize.height = renderCanvas.height;
standardSize.width = renderCanvas.width;
}
const pxRatio = this.#container.retina.pixelRatio, retinaSize = this.size;
renderCanvas.height = retinaSize.height = standardSize.height * pxRatio;
renderCanvas.width = retinaSize.width = standardSize.width * pxRatio;
}
resize() {
const element = this.domElement;
if (!element) {
return false;
}
const container = this.#container, renderCanvas = this.renderCanvas;
if (renderCanvas === undefined) {
return false;
}
const currentSize = container.canvas.#standardSize, newSize = {
width: element.offsetWidth,
height: element.offsetHeight,
}, pxRatio = container.retina.pixelRatio, retinaSize = {
width: newSize.width * pxRatio,
height: newSize.height * pxRatio,
};
if (newSize.height === currentSize.height &&
newSize.width === currentSize.width &&
retinaSize.height === renderCanvas.height &&
retinaSize.width === renderCanvas.width) {
return false;
}
const oldSize = { ...currentSize };
currentSize.height = newSize.height;
currentSize.width = newSize.width;
const canvasSize = this.size;
renderCanvas.width = canvasSize.width = retinaSize.width;
renderCanvas.height = canvasSize.height = retinaSize.height;
if (this.#container.started) {
container.particles.setResizeFactor({
width: currentSize.width / oldSize.width,
height: currentSize.height / oldSize.height,
});
}
return true;
}
setPointerEvents(type) {
const element = this.domElement;
if (!element) {
return;
}
this.#pointerEvents = type;
this.#repairStyle();
}
setZoom(zoomLevel, center) {
this.zoom = zoomLevel;
this.#zoomCenter = center;
}
stop() {
this.#safeMutationObserver(obs => {
obs.disconnect();
});
this.#mutationObserver = undefined;
this.#removeHdrListeners();
this.render.stop();
}
async windowResize() {
if (!this.domElement || !this.resize()) {
return;
}
const container = this.#container, needsRefresh = container.updateActualOptions();
container.particles.setDensity();
this.#applyResizePlugins();
if (needsRefresh) {
await container.refresh();
}
}
#applyResizePlugins() {
for (const plugin of this.#resizePlugins) {
plugin.resize?.();
}
}
#initContext() {
const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
safeMatchMedia("(color-gamut: p3)")?.matches &&
safeMatchMedia("(dynamic-range: high)")?.matches;
container.hdr = canSupportHdr ?? false;
container.hdrMode = container.actualOptions.hdr.mode;
container.peakNits = container.actualOptions.hdr.peakNits;
const renderCanvas = this.renderCanvas;
if (!renderCanvas) {
return;
}
this.render.setContextSettings({
alpha: true,
desynchronized: true,
willReadFrequently: false,
...(canSupportHdr
? { colorSpace: "display-p3", colorType: "float16" }
: { colorSpace: "srgb" }),
});
let context;
try {
context = renderCanvas.getContext("2d", this.render.settings);
}
catch {
context = null;
}
if (canSupportHdr && !context) {
container.hdr = false;
const sdrSettings = {
alpha: true,
desynchronized: true,
willReadFrequently: false,
colorSpace: "srgb",
};
this.render.setContextSettings(sdrSettings);
try {
context = renderCanvas.getContext("2d", sdrSettings);
}
catch {
context = null;
}
}
this.render.setContext(context);
}
#initHdrListeners() {
this.#removeHdrListeners();
const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
this.#recreateRenderCanvas();
this.#initContext();
this.initBackground();
}, listeners = [];
if (p3Query) {
p3Query.addEventListener("change", handleChange);
listeners.push({ handler: handleChange, mql: p3Query });
}
if (hdrQuery) {
hdrQuery.addEventListener("change", handleChange);
listeners.push({ handler: handleChange, mql: hdrQuery });
}
this.#hdrMediaListeners = listeners;
}
#initStyle() {
const element = this.domElement, options = this.#container.actualOptions;
if (!element) {
return;
}
if (this.#fullScreen) {
this.#setFullScreenStyle();
}
else {
this.#resetOriginalStyle();
}
for (const key in options.style) {
if (!key || !(key in options.style)) {
continue;
}
const value = options.style[key];
if (!value) {
continue;
}
element.style.setProperty(key, value, "important");
}
}
#recreateRenderCanvas() {
const renderCanvas = this.renderCanvas;
if (!renderCanvas) {
return;
}
if (this.domElement) {
return;
}
this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
}
#removeHdrListeners() {
if (!this.#hdrMediaListeners) {
return;
}
for (const { handler, mql } of this.#hdrMediaListeners) {
mql.removeEventListener("change", handler);
}
this.#hdrMediaListeners = undefined;
}
#repairStyle() {
const element = this.domElement;
if (!element) {
return;
}
this.#safeMutationObserver(observer => {
observer.disconnect();
});
this.#initStyle();
this.initBackground();
const pointerEvents = this.#pointerEvents;
element.style.pointerEvents = pointerEvents;
element.style.setProperty("pointer-events", pointerEvents);
this.#safeMutationObserver(observer => {
if (!(element instanceof Node)) {
return;
}
observer.observe(element, { attributes: true });
});
}
#resetOriginalStyle() {
const element = this.domElement, originalStyle = this.#originalStyle;
if (!element || !originalStyle) {
return;
}
setStyle(element, originalStyle, true);
}
#safeMutationObserver(callback) {
if (!this.#mutationObserver) {
return;
}
callback(this.#mutationObserver);
}
#setFullScreenStyle() {
const element = this.domElement;
if (!element) {
return;
}
setStyle(element, getFullScreenStyle(this.#container.actualOptions.fullScreen.zIndex), true);
}
}
class EventListeners {
#container;
#handlers;
#resizeObserver;
#resizeTimeout;
constructor(container) {
this.#container = container;
this.#handlers = {
visibilityChange: () => {
this.#handleVisibilityChange();
},
resize: () => {
this.#handleWindowResize();
},
};
}
addListeners() {
this.#manageListeners(true);
}
removeListeners() {
this.#manageListeners(false);
}
#handleVisibilityChange() {
const container = this.#container, options = container.actualOptions;
if (!options.pauseOnBlur) {
return;
}
if (safeDocument().hidden) {
container.pageHidden = true;
container.pause();
}
else {
container.pageHidden = false;
if (container.animationStatus) {
container.play(true);
}
else {
container.draw(true);
}
}
}
#handleWindowResize() {
if (this.#resizeTimeout) {
clearTimeout(this.#resizeTimeout);
this.#resizeTimeout = undefined;
}
const handleResize = async () => {
const canvas = this.#container.canvas;
await canvas.windowResize();
};
this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds$1);
}
#manageListeners(add) {
const handlers = this.#handlers;
this.#manageResize(add);
manageListener(document, visibilityChangeEvent, handlers.visibilityChange, add, false);
}
#manageResize(add) {
const handlers = this.#handlers, container = this.#container, options = container.actualOptions;
if (!options.resize.enable) {
return;
}
if (typeof ResizeObserver === "undefined") {
manageListener(globalThis, resizeEvent, handlers.resize, add);
return;
}
const canvasEl = container.canvas.domElement;
if (this.#resizeObserver && !add) {
if (canvasEl) {
this.#resizeObserver.unobserve(canvasEl);
}
this.#resizeObserver.disconnect();
this.#resizeObserver = undefined;
}
else if (!this.#resizeObserver && add && canvasEl) {
this.#resizeObserver = new ResizeObserver((entries) => {
const entry = entries.find(e => e.target === canvasEl);
if (!entry) {
return;
}
this.#handleWindowResize();
});
this.#resizeObserver.observe(canvasEl);
}
}
}
function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
const effectData = effectOptions.options[effect];
return deepExtend({
close: effectOptions.close,
}, itemFromSingleOrMultiple(effectData, id, reduceDuplicates));
}
function loadShapeData(shape, shapeOptions, id, reduceDuplicates) {
const shapeData = shapeOptions.options[shape];
return deepExtend({
close: shapeOptions.close,
}, itemFromSingleOrMultiple(shapeData, id, reduceDuplicates));
}
function fixOutMode(data) {
if (!isInArray(data.outMode, data.checkModes)) {
return;
}
const diameter = data.radius * double;
if (data.coord > data.maxCoord - diameter) {
data.setCb(-data.radius);
}
else if (data.coord < diameter) {
data.setCb(data.radius);
}
}
function normalizeAngle(angle, modulus) {
const normalized = angle % modulus;
return normalized < defaultAngle$1 ? normalized + modulus : normalized;
}
function initParticleState(particle, id, group) {
particle.id = id;
particle.group = group;
particle.justWarped = false;
particle.effectClose = true;
particle.shapeClose = true;
particle.pathRotation = false;
particle.lastPathTime = 0;
particle.destroyed = false;
particle.unbreakable = false;
particle.isRotating = false;
particle.rotation = 0;
particle.misplaced = false;
particle.retina = {
maxDistance: {},
maxSpeed: 0,
moveDrift: 0,
moveSpeed: 0,
sizeAnimationSpeed: 0,
};
particle.size = {
value: 1,
max: 1,
min: 1,
enable: false,
};
particle.outType = exports.ParticleOutType.normal;
particle.ignoresResizeRatio = true;
}
function resolveParticleOptions(particle, container, pluginManager, overrideOptions) {
const mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(pluginManager, container, mainOptions.particles), reduceDuplicates = particlesOptions.reduceDuplicates;
particle.effect = itemFromSingleOrMultiple(particlesOptions.effect.type, particle.id, reduceDuplicates);
particle.shape = itemFromSingleOrMultiple(particlesOptions.shape.type, particle.id, reduceDuplicates);
const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
if (overrideOptions) {
if (overrideOptions.effect) {
const overrideEffectType = overrideOptions.effect.type;
if (overrideEffectType && overrideEffectType !== particle.effect) {
const effect = itemFromSingleOrMultiple(overrideEffectType, particle.id, reduceDuplicates);
if (effect) {
particle.effect = effect;
}
}
effectOptions.load(overrideOptions.effect);
}
if (overrideOptions.shape) {
const overrideShapeType = overrideOptions.shape.type;
if (overrideShapeType && overrideShapeType !== particle.shape) {
const shape = itemFromSingleOrMultiple(overrideShapeType, particle.id, reduceDuplicates);
if (shape) {
particle.shape = shape;
}
}
shapeOptions.load(overrideOptions.shape);
}
}
if (particle.effect === randomColorValue) {
const availableEffects = [...container.effectDrawers.keys()];
particle.effect = availableEffects[Math.floor(getRandom() * availableEffects.length)];
}
if (particle.shape === randomColorValue) {
const availableShapes = [...container.shapeDrawers.keys()];
particle.shape = availableShapes[Math.floor(getRandom() * availableShapes.length)];
}
particle.effectData = particle.effect
? loadEffectData(particle.effect, effectOptions, particle.id, reduceDuplicates)
: undefined;
particle.shapeData = particle.shape
? loadShapeData(particle.shape, shapeOptions, particle.id, reduceDuplicates)
: undefined;
particlesOptions.load(overrideOptions);
const effectData = particle.effectData, shapeData = particle.shapeData;
if (effectData) {
particlesOptions.load(effectData.particles);
}
if (shapeData) {
particlesOptions.load(shapeData.particles);
}
particle.effectClose = effectData?.close ?? particlesOptions.effect.close;
particle.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
return particlesOptions;
}
function initParticleDrawers(particle, container) {
let effectDrawer, shapeDrawer;
if (particle.effect) {
effectDrawer = container.effectDrawers.get(particle.effect);
}
if (effectDrawer?.loadEffect) {
effectDrawer.loadEffect(particle);
}
if (particle.shape) {
shapeDrawer = container.shapeDrawers.get(particle.shape);
}
if (shapeDrawer?.loadShape) {
shapeDrawer.loadShape(particle);
}
const sideCountFunc = shapeDrawer?.getSidesCount;
if (sideCountFunc) {
particle.sides = sideCountFunc(particle);
}
}
function runUpdaterPreInit(updaters, particle) {
for (const updater of updaters) {
updater.preInit?.(particle);
}
}
function runUpdaterInit(updaters, particle) {
for (const updater of updaters) {
updater.init(particle);
}
}
function runDrawerInit(container, particle) {
const shapeDrawer = particle.shape ? container.shapeDrawers.get(particle.shape) : undefined, effectDrawer = particle.effect ? container.effectDrawers.get(particle.effect) : undefined;
effectDrawer?.particleInit?.(container, particle);
shapeDrawer?.particleInit?.(container, particle);
}
function runParticleCreatedPlugins(container, particle) {
for (const plugin of container.particleCreatedPlugins) {
plugin.particleCreated?.(particle);
}
}
class Particle {
backColor;
destroyed;
direction;
effect;
effectClose;
effectData;
fillColor;
fillEnabled;
fillOpacity;
group;
id;
ignoresResizeRatio;
initialPosition;
initialVelocity;
isRotating;
justWarped;
lastPathTime;
misplaced;
moveCenter;
offset;
opacity;
options;
outType;
pathRotation;
position;
randomIndexData;
retina;
roll;
rotation;
shape;
shapeClose;
shapeData;
sides;
size;
spawning;
strokeColor;
strokeOpacity;
strokeWidth;
unbreakable;
velocity;
zIndexFactor;
#cachedOpacityData = {
fillOpacity: defaultOpacity$3,
opacity: defaultOpacity$3,
strokeOpacity: defaultOpacity$3,
};
#cachedPosition = Vector3d.origin;
#cachedRotateData = { sin: 0, cos: 0 };
#cachedTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};
#container;
#modifiers = [];
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
addModifier(modifier) {
this.#modifiers.push(modifier);
this.#modifiers.sort((a, b) => a.priority - b.priority);
}
clearModifiers() {
this.#modifiers.length = 0;
}
destroy(override) {
if (this.unbreakable || this.destroyed) {
return;
}
this.destroyed = true;
this.clearModifiers();
const container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
shapeDrawer?.particleDestroy?.(this);
for (const plugin of container.particleDestroyedPlugins) {
plugin.particleDestroyed?.(this, override);
}
for (const updater of container.particleUpdaters) {
updater.particleDestroyed?.(this, override);
}
this.#container.dispatchEvent(exports.EventType.particleDestroyed, {
particle: this,
});
}
draw(delta) {
const container = this.#container, render = container.canvas.render;
render.drawParticlePlugins(this, delta);
render.drawParticle(this, delta);
}
getAngle() {
return this.rotation + (this.pathRotation ? this.velocity.angle : defaultAngle$1);
}
getFillColor() {
return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.fillColor), m => m.fillColor));
}
getMass() {
return this.getRadius() ** squareExp * Math.PI * half$2;
}
getModifier(id) {
return this.#modifiers.find(m => m.id === id);
}
getOpacity() {
const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, baseOpacity = getRangeValue(this.opacity?.value ?? defaultOpacity$3), modifierOpacity = this.#applyModifiers(undefined, m => m.opacity), opacity = modifierOpacity ?? baseOpacity, fillOpacity = this.fillOpacity ?? defaultOpacity$3, strokeOpacity = this.strokeOpacity ?? defaultOpacity$3;
this.#cachedOpacityData.fillOpacity = fillOpacity;
this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
this.#cachedOpacityData.strokeOpacity = strokeOpacity;
return this.#cachedOpacityData;
}
getPosition() {
this.#cachedPosition.x = this.position.x + this.offset.x;
this.#cachedPosition.y = this.position.y + this.offset.y;
this.#cachedPosition.z = this.position.z;
return this.#cachedPosition;
}
getRadius() {
return this.#applyModifiers(this.size.value, m => m.radius);
}
getRotateData() {
const angle = this.getAngle();
this.#cachedRotateData.sin = Math.sin(angle);
this.#cachedRotateData.cos = Math.cos(angle);
return this.#cachedRotateData;
}
getStrokeColor() {
return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.strokeColor), m => m.strokeColor));
}
getTransformData(externalTransform) {
const rotateData = this.getRotateData(), rotating = this.isRotating;
this.#cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform$1.a);
this.#cachedTransform.b = rotating
? rotateData.sin * (externalTransform.b ?? identity$3)
: (externalTransform.b ?? defaultTransform$1.b);
this.#cachedTransform.c = rotating
? -rotateData.sin * (externalTransform.c ?? identity$3)
: (externalTransform.c ?? defaultTransform$1.c);
this.#cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform$1.d);
return this.#cachedTransform;
}
init(id, position, overrideOptions, group) {
const container = this.#container;
initParticleState(this, id, group);
this.options = resolveParticleOptions(this, container, this.#pluginManager, overrideOptions);
container.retina.initParticle(this);
runUpdaterPreInit(container.particleUpdaters, this);
this.#initPosition(position);
this.initialVelocity = this.#calculateVelocity();
this.velocity = this.initialVelocity.copy();
this.zIndexFactor = this.position.z / container.zLayers;
this.sides = 24;
initParticleDrawers(this, container);
this.spawning = false;
runUpdaterInit(container.particleUpdaters, this);
runDrawerInit(container, this);
runParticleCreatedPlugins(container, this);
}
isInsideCanvas(direction) {
return this.#getInsideCanvasResult({ direction }).inside;
}
isInsideCanvasForOutMode(outMode, direction) {
return this.#getInsideCanvasResult({ direction, outMode }).inside;
}
isShowingBack() {
if (!this.roll) {
return false;
}
const angle = this.roll.angle;
if (this.roll.horizontal && this.roll.vertical) {
const adjustedAngle = normalizeAngle(angle, doublePI);
return adjustedAngle >= Math.PI * half$2 && adjustedAngle < Math.PI * triple * half$2;
}
if (this.roll.horizontal) {
const adjustedAngle = normalizeAngle(angle + Math.PI * half$2, doublePI);
return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
}
if (this.roll.vertical) {
const adjustedAngle = normalizeAngle(angle, doublePI);
return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
}
return false;
}
isVisible() {
return !this.destroyed && !this.spawning && this.isInsideCanvas();
}
removeModifier(id) {
const idx = this.#modifiers.findIndex(m => m.id === id);
if (idx >= defaultAngle$1) {
this.#modifiers.splice(idx, identity$3);
}
}
reset() {
for (const updater of this.#container.particleUpdaters) {
updater.reset?.(this);
}
}
#applyModifiers(base, getter) {
let value = base;
for (const mod of this.#modifiers) {
if (mod.enabled) {
const override = getter(mod);
if (override !== undefined) {
value = override;
}
}
}
return value;
}
#calcPosition(position, zIndex) {
let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
const container = this.#container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size;
for (;;) {
for (const plugin of plugins) {
const pluginPos = plugin.particlePosition?.(posVec, this);
if (pluginPos) {
return Vector3d.create(pluginPos.x, pluginPos.y, zIndex);
}
}
const exactPosition = calcExactPositionOrRandomFromSize({
size: canvasSize,
position: posVec,
}), pos = Vector3d.create(exactPosition.x, exactPosition.y, zIndex);
this.#fixHorizontal(pos, radius, outModes.left ?? outModes.default);
this.#fixHorizontal(pos, radius, outModes.right ?? outModes.default);
this.#fixVertical(pos, radius, outModes.top ?? outModes.default);
this.#fixVertical(pos, radius, outModes.bottom ?? outModes.default);
let isValidPosition = true;
for (const plugin of container.particles.checkParticlePositionPlugins) {
isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount) ?? true;
if (!isValidPosition) {
break;
}
}
if (isValidPosition) {
return pos;
}
tryCount += tryCountIncrement;
posVec = undefined;
}
}
#calculateVelocity() {
const moveOptions = this.options.move, baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy();
if (moveOptions.direction === exports.MoveDirection.inside || moveOptions.direction === exports.MoveDirection.outside) {
return res;
}
const rad = degToRad(getRangeValue(moveOptions.angle.value)), radOffset = degToRad(getRangeValue(moveOptions.angle.offset)), range = {
left: radOffset - rad * half$2,
right: radOffset + rad * half$2,
};
if (!moveOptions.straight) {
res.angle += randomInRangeValue(setRangeValue(range.left, range.right));
}
if (moveOptions.random && typeof moveOptions.speed === "number") {
res.length *= getRandom();
}
return res;
}
#fixHorizontal(pos, radius, outMode) {
fixOutMode({
outMode,
checkModes: [exports.OutMode.bounce],
coord: pos.x,
maxCoord: this.#container.canvas.size.width,
setCb: (value) => (pos.x += value),
radius,
});
}
#fixVertical(pos, radius, outMode) {
fixOutMode({
outMode,
checkModes: [exports.OutMode.bounce],
coord: pos.y,
maxCoord: this.#container.canvas.size.height,
setCb: (value) => (pos.y += value),
radius,
});
}
#getDefaultInsideCanvasResult(direction, outMode) {
const radius = this.getRadius(), canvasSize = this.#container.canvas.size, position = this.position, isBounce = outMode === exports.OutMode.bounce;
if (direction === exports.OutModeDirection.bottom) {
return {
inside: isBounce ? position.y + radius < canvasSize.height : position.y - radius < canvasSize.height,
reason: "default",
};
}
if (direction === exports.OutModeDirection.left) {
return {
inside: isBounce ? position.x - radius > defaultAngle$1 : position.x + radius > defaultAngle$1,
reason: "default",
};
}
if (direction === exports.OutModeDirection.right) {
return {
inside: isBounce ? position.x + radius < canvasSize.width : position.x - radius < canvasSize.width,
reason: "default",
};
}
if (direction === exports.OutModeDirection.top) {
return {
inside: isBounce ? position.y - radius > defaultAngle$1 : position.y + radius > defaultAngle$1,
reason: "default",
};
}
return {
inside: position.x >= -radius &&
position.y >= -radius &&
position.y <= canvasSize.height + radius &&
position.x <= canvasSize.width + radius,
reason: "default",
};
}
#getInsideCanvasCallbackData(direction, outMode) {
return {
canvasSize: this.#container.canvas.size,
direction,
outMode,
particle: this,
radius: this.getRadius(),
};
}
#getInsideCanvasResult(data) {
const defaultResult = this.#getDefaultInsideCanvasResult(data.direction, data.outMode), container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined, effectDrawer = this.effect ? container.effectDrawers.get(this.effect) : undefined, shapeCheck = shapeDrawer?.isInsideCanvas, effectCheck = effectDrawer?.isInsideCanvas;
if (!shapeCheck && !effectCheck) {
return defaultResult;
}
const callbackData = this.#getInsideCanvasCallbackData(data.direction, data.outMode), shapeResult = shapeCheck ? this.#normalizeInsideCanvasResult(shapeCheck(callbackData), "shape") : undefined, effectResult = effectCheck ? this.#normalizeInsideCanvasResult(effectCheck(callbackData), "effect") : undefined;
if (shapeResult && effectResult) {
const margin = Math.max(shapeResult.margin ?? defaultAngle$1, effectResult.margin ?? defaultAngle$1);
return {
inside: shapeResult.inside && effectResult.inside,
margin: margin > defaultAngle$1 ? margin : undefined,
reason: "combined",
};
}
return shapeResult ?? effectResult ?? defaultResult;
}
#getRollColor(color) {
if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
return color;
}
if (!this.isShowingBack()) {
return color;
}
if (this.backColor) {
return this.backColor;
}
if (this.roll.alter) {
return alterHsl(color, this.roll.alter.type, this.roll.alter.value);
}
return color;
}
#initPosition(position) {
const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
if (!initialPosition) {
throw new Error("a valid position cannot be found for particle");
}
this.position = initialPosition;
this.initialPosition = this.position.copy();
const canvasSize = container.canvas.size;
this.moveCenter = {
...getPosition(this.options.move.center, canvasSize),
radius: this.options.move.center.radius,
mode: this.options.move.center.mode,
};
this.direction = getParticleDirectionAngle(this.options.move.direction, this.position, this.moveCenter);
switch (this.options.move.direction) {
case exports.MoveDirection.inside:
this.outType = exports.ParticleOutType.inside;
break;
case exports.MoveDirection.outside:
this.outType = exports.ParticleOutType.outside;
break;
}
this.offset = Vector.origin;
}
#normalizeInsideCanvasResult(result, reason) {
if (typeof result === "boolean") {
return {
inside: result,
reason,
};
}
return {
inside: result.inside,
margin: result.margin,
reason: result.reason ?? reason,
};
}
}
class SpatialHashGrid {
#cellSize;
#cells = new Map();
#circlePool = [];
#circlePoolIdx;
#pendingCellSize;
#rectanglePool = [];
#rectanglePoolIdx;
constructor(cellSize) {
this.#cellSize = cellSize;
this.#circlePoolIdx = 0;
this.#rectanglePoolIdx = 0;
}
clear() {
this.#cells.clear();
const pendingCellSize = this.#pendingCellSize;
if (pendingCellSize) {
this.#cellSize = pendingCellSize;
}
this.#pendingCellSize = undefined;
}
insert(particle) {
const { x, y } = particle.getPosition(), key = this.#cellKeyFromCoords(x, y);
if (!this.#cells.has(key)) {
this.#cells.set(key, []);
}
this.#cells.get(key)?.push(particle);
}
query(range, check, out = []) {
const bounds = this.#getRangeBounds(range);
if (!bounds) {
return out;
}
const minCellX = Math.floor(bounds.minX / this.#cellSize), maxCellX = Math.floor(bounds.maxX / this.#cellSize), minCellY = Math.floor(bounds.minY / this.#cellSize), maxCellY = Math.floor(bounds.maxY / this.#cellSize);
for (let cx = minCellX; cx <= maxCellX; cx++) {
for (let cy = minCellY; cy <= maxCellY; cy++) {
const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
if (!cellParticles) {
continue;
}
for (const p of cellParticles) {
if (check && !check(p)) {
continue;
}
if (range.contains(p.getPosition())) {
out.push(p);
}
}
}
}
return out;
}
queryCircle(position, radius, check, out = []) {
const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
this.#releaseShapes();
return result;
}
queryRectangle(position, size, check, out = []) {
const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
this.#releaseShapes();
return result;
}
setCellSize(cellSize) {
this.#pendingCellSize = cellSize;
}
#acquireCircle(x, y, r) {
return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
}
#acquireRectangle(x, y, w, h) {
return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
}
#cellKeyFromCoords(x, y) {
const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
return `${cellX}_${cellY}`;
}
#getRangeBounds(range) {
if (range instanceof Circle) {
const r = range.radius, { x, y } = range.position;
return {
minX: x - r,
maxX: x + r,
minY: y - r,
maxY: y + r,
};
}
if (range instanceof Rectangle) {
const { x, y } = range.position, { width, height } = range.size;
return {
minX: x,
maxX: x + width,
minY: y,
maxY: y + height,
};
}
return null;
}
#releaseShapes() {
this.#circlePoolIdx = 0;
this.#rectanglePoolIdx = 0;
}
}
class ParticlesManager {
checkParticlePositionPlugins;
grid;
#array;
#container;
#groupLimits;
#limit;
#nextId;
#particleBuckets;
#particleResetPlugins;
#particleUpdatePlugins;
#pluginManager;
#pool;
#postParticleUpdatePlugins;
#postUpdatePlugins;
#resizeFactor;
#updatePlugins;
#zBuckets;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#nextId = 0;
this.#array = [];
this.#pool = [];
this.#limit = 0;
this.#groupLimits = new Map();
this.#particleBuckets = new Map();
this.#zBuckets = this.#createBuckets(this.#container.zLayers);
this.grid = new SpatialHashGrid(spatialHashGridCellSize);
this.checkParticlePositionPlugins = [];
this.#particleResetPlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#postParticleUpdatePlugins = [];
this.#updatePlugins = [];
}
get count() {
return this.#array.length;
}
addParticle(position, overrideOptions, group, initializer) {
const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
if (limit > minLimit) {
switch (limitMode) {
case exports.LimitMode.delete: {
const countToRemove = currentCount + countOffset - limit;
if (countToRemove > minCount) {
this.removeQuantity(countToRemove);
}
break;
}
case exports.LimitMode.wait:
if (currentCount >= limit) {
return;
}
break;
}
}
try {
const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
particle.init(this.#nextId, position, overrideOptions, group);
let canAdd = true;
if (initializer) {
canAdd = initializer(particle);
}
if (!canAdd) {
this.#pool.push(particle);
return;
}
this.#array.push(particle);
this.#insertParticleIntoBucket(particle);
this.#nextId++;
this.#container.dispatchEvent(exports.EventType.particleAdded, {
particle,
});
return particle;
}
catch (e) {
getLogger().warning(`error adding particle: ${e}`);
}
return undefined;
}
clear() {
this.#array = [];
this.#particleBuckets.clear();
this.#resetBuckets(this.#container.zLayers);
}
destroy() {
this.#array = [];
this.#pool.length = 0;
this.#particleBuckets.clear();
this.#zBuckets = [];
this.checkParticlePositionPlugins = [];
this.#particleResetPlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#postParticleUpdatePlugins = [];
this.#updatePlugins = [];
}
drawParticles(delta) {
for (let i = this.#zBuckets.length - one; i >= minIndex$1; i--) {
const bucket = this.#zBuckets[i];
if (!bucket) {
continue;
}
for (const particle of bucket) {
particle.draw(delta);
}
}
}
filter(condition) {
return this.#array.filter(condition);
}
find(condition) {
return this.#array.find(condition);
}
get(index) {
return this.#array[index];
}
async init() {
const container = this.#container, options = container.actualOptions;
this.checkParticlePositionPlugins = [];
this.#updatePlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#particleResetPlugins = [];
this.#postParticleUpdatePlugins = [];
this.#particleBuckets.clear();
this.#resetBuckets(container.zLayers);
this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
for (const plugin of container.plugins) {
if (plugin.redrawInit) {
await plugin.redrawInit();
}
if (plugin.checkParticlePosition) {
this.checkParticlePositionPlugins.push(plugin);
}
if (plugin.update) {
this.#updatePlugins.push(plugin);
}
if (plugin.particleUpdate) {
this.#particleUpdatePlugins.push(plugin);
}
if (plugin.postUpdate) {
this.#postUpdatePlugins.push(plugin);
}
if (plugin.particleReset) {
this.#particleResetPlugins.push(plugin);
}
if (plugin.postParticleUpdate) {
this.#postParticleUpdatePlugins.push(plugin);
}
}
await this.#container.initDrawersAndUpdaters();
for (const drawer of this.#container.effectDrawers.values()) {
await drawer.init?.(container);
}
for (const drawer of this.#container.shapeDrawers.values()) {
await drawer.init?.(container);
}
let handled = false;
for (const plugin of container.plugins) {
handled = plugin.particlesInitialization?.() ?? handled;
if (handled) {
break;
}
}
if (!handled) {
const particlesOptions = options.particles, groups = particlesOptions.groups;
for (const group in groups) {
const groupOptions = groups[group];
if (!groupOptions) {
continue;
}
for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
this.addParticle(undefined, groupOptions, group);
}
}
for (let i = this.count; i < particlesOptions.number.value; i++) {
this.addParticle();
}
}
}
push(nb, position, overrideOptions, group) {
for (let i = 0; i < nb; i++) {
this.addParticle(position, overrideOptions, group);
}
}
async redraw() {
this.clear();
await this.init();
this.#container.canvas.render.drawParticles({ value: 0, factor: 0 });
}
remove(particle, group, override) {
this.removeAt(this.#array.indexOf(particle), undefined, group, override);
}
removeAt(index, quantity = defaultRemoveQuantity, group, override) {
if (index < minIndex$1 || index > this.count) {
return;
}
let deleted = 0;
for (let i = index; deleted < quantity && i < this.count; i++) {
if (this.#removeParticle(i, group, override)) {
i--;
deleted++;
}
}
}
removeQuantity(quantity, group) {
this.removeAt(minIndex$1, quantity, group);
}
setDensity() {
const options = this.#container.actualOptions, groups = options.particles.groups;
let pluginsCount = 0;
for (const plugin of this.#container.plugins) {
if (plugin.particlesDensityCount) {
pluginsCount += plugin.particlesDensityCount();
}
}
for (const group in groups) {
const groupData = groups[group];
if (!groupData) {
continue;
}
const groupDataOptions = loadParticlesOptions(this.#pluginManager, this.#container, groupData);
this.#applyDensity(groupDataOptions, pluginsCount, group);
}
this.#applyDensity(options.particles, pluginsCount);
}
setResizeFactor(factor) {
this.#resizeFactor = factor;
}
update(delta) {
this.grid.clear();
for (const plugin of this.#updatePlugins) {
plugin.update?.(delta);
}
const particlesToDelete = this.#updateParticlesPhase1(delta);
for (const plugin of this.#postUpdatePlugins) {
plugin.postUpdate?.(delta);
}
this.#updateParticlesPhase2(delta, particlesToDelete);
if (particlesToDelete.size) {
for (const particle of particlesToDelete) {
this.remove(particle);
}
}
this.#resizeFactor = undefined;
}
#addToPool(...particles) {
this.#pool.push(...particles);
}
#applyDensity(options, pluginsCount, group, groupOptions) {
const numberOptions = options.number;
if (!numberOptions.density.enable) {
if (group === undefined) {
this.#limit = numberOptions.limit.value;
}
else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
}
return;
}
const densityFactor = this.#initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit.value > minLimit ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + pluginsCount, particlesCount = Math.min(this.count, this.filter(t => t.group === group).length);
if (group === undefined) {
this.#limit = numberOptions.limit.value * densityFactor;
}
else {
this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
}
if (particlesCount < particlesNumber) {
this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
}
else if (particlesCount > particlesNumber) {
this.removeQuantity(particlesCount - particlesNumber, group);
}
}
#createBuckets(zLayers) {
const bucketCount = Math.max(Math.floor(zLayers), one);
return Array.from({ length: bucketCount }, () => []);
}
#getBucketIndex(zIndex) {
const maxBucketIndex = this.#zBuckets.length - one;
if (maxBucketIndex <= minIndex$1) {
return minIndex$1;
}
return Math.min(Math.max(Math.floor(zIndex), minIndex$1), maxBucketIndex);
}
#initDensityFactor(densityOptions) {
const container = this.#container;
if (!densityOptions.enable) {
return defaultDensityFactor;
}
const canvasSize = container.canvas.size, pxRatio = container.retina.pixelRatio;
if (!canvasSize.width || !canvasSize.height) {
return defaultDensityFactor;
}
return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
}
#insertParticleIntoBucket(particle) {
const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
if (!bucket) {
return;
}
bucket.push(particle);
this.#particleBuckets.set(particle.id, bucketIndex);
}
#removeParticle(index, group, override) {
const particle = this.#array[index];
if (!particle) {
return false;
}
if (particle.group !== group) {
return false;
}
this.#array.splice(index, deleteCount);
this.#removeParticleFromBucket(particle);
particle.destroy(override);
this.#container.dispatchEvent(exports.EventType.particleRemoved, {
particle,
});
this.#addToPool(particle);
return true;
}
#removeParticleFromBucket(particle) {
const bucketIndex = this.#particleBuckets.get(particle.id) ?? this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
if (!bucket) {
this.#particleBuckets.delete(particle.id);
return;
}
const idx = bucket.findIndex(p => p.id === particle.id);
if (idx >= minIndex$1) {
bucket.splice(idx, deleteCount);
}
this.#particleBuckets.delete(particle.id);
}
#resetBuckets(zLayers) {
const bucketCount = Math.max(Math.floor(zLayers), one);
if (this.#zBuckets.length !== bucketCount) {
this.#zBuckets = this.#createBuckets(bucketCount);
return;
}
for (const bucket of this.#zBuckets) {
bucket.length = minIndex$1;
}
}
#updateParticleBucket(particle) {
const newBucketIndex = this.#getBucketIndex(particle.position.z), currentBucketIndex = this.#particleBuckets.get(particle.id);
if (currentBucketIndex === undefined) {
this.#insertParticleIntoBucket(particle);
return;
}
if (currentBucketIndex === newBucketIndex) {
return;
}
const currentBucket = this.#zBuckets[currentBucketIndex];
if (currentBucket) {
const idx = currentBucket.findIndex(p => p.id === particle.id);
if (idx >= minIndex$1) {
currentBucket.splice(idx, deleteCount);
}
}
const newBucket = this.#zBuckets[newBucketIndex];
if (!newBucket) {
this.#particleBuckets.set(particle.id, newBucketIndex);
return;
}
newBucket.push(particle);
if (newBucket.length >= double) {
const prev = newBucket[newBucket.length - double];
if (prev && particle.id < prev.id) {
newBucket.sort((a, b) => a.id - b.id);
}
}
this.#particleBuckets.set(particle.id, newBucketIndex);
}
#updateParticlesPhase1(delta) {
const particlesToDelete = new Set(), resizeFactor = this.#resizeFactor;
for (const particle of this.#array) {
if (resizeFactor && !particle.ignoresResizeRatio) {
particle.position.x *= resizeFactor.width;
particle.position.y *= resizeFactor.height;
particle.initialPosition.x *= resizeFactor.width;
particle.initialPosition.y *= resizeFactor.height;
}
particle.ignoresResizeRatio = false;
for (const plugin of this.#particleResetPlugins) {
plugin.particleReset?.(particle);
}
for (const plugin of this.#particleUpdatePlugins) {
if (particle.destroyed) {
break;
}
plugin.particleUpdate?.(particle, delta);
}
if (particle.destroyed) {
particlesToDelete.add(particle);
continue;
}
this.grid.insert(particle);
}
return particlesToDelete;
}
#updateParticlesPhase2(delta, particlesToDelete) {
for (const particle of this.#array) {
if (particle.destroyed) {
particlesToDelete.add(particle);
continue;
}
for (const updater of this.#container.particleUpdaters) {
updater.update(particle, delta);
}
if (!particle.spawning) {
for (const plugin of this.#postParticleUpdatePlugins) {
plugin.postParticleUpdate?.(particle, delta);
}
}
this.#updateParticleBucket(particle);
}
}
}
class Retina {
pixelRatio;
reduceFactor;
#container;
constructor(container) {
this.#container = container;
this.pixelRatio = defaultRatio$1;
this.reduceFactor = defaultReduceFactor;
}
init() {
const container = this.#container, options = container.actualOptions;
this.pixelRatio = options.detectRetina ? devicePixelRatio : defaultRatio$1;
this.reduceFactor = defaultReduceFactor;
const ratio = this.pixelRatio, canvas = container.canvas, element = canvas.domElement;
if (element) {
canvas.size.width = element.offsetWidth * ratio;
canvas.size.height = element.offsetHeight * ratio;
}
}
initParticle(particle) {
const options = particle.options, ratio = this.pixelRatio, moveOptions = options.move, moveDistance = moveOptions.distance, props = particle.retina;
props.maxSpeed = getRangeValue(moveOptions.gravity.maxSpeed) * ratio;
props.moveDrift = getRangeValue(moveOptions.drift) * ratio;
props.moveSpeed = getRangeValue(moveOptions.speed) * ratio;
const maxDistance = props.maxDistance;
maxDistance.horizontal = moveDistance.horizontal === undefined ? undefined : moveDistance.horizontal * ratio;
maxDistance.vertical = moveDistance.vertical === undefined ? undefined : moveDistance.vertical * ratio;
}
}
function guardCheck(container) {
return !container.destroyed;
}
function updateDelta(delta, value, fpsLimit = defaultFps, smooth = false) {
delta.value = value;
delta.factor = smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds$1;
}
function loadContainerOptions(pluginManager, container, ...sourceOptionsArr) {
const options = new Options(pluginManager, container);
loadOptions(options, ...sourceOptionsArr);
return options;
}
class Container {
actualOptions;
canvas;
destroyed;
effectDrawers;
fpsLimit;
hdr;
hdrMode;
id;
pageHidden;
particleCreatedPlugins;
particleDestroyedPlugins;
particlePositionPlugins;
particleUpdaters;
particles;
peakNits;
plugins;
retina;
shapeDrawers;
started;
zLayers;
#delay;
#delayTimeout;
#delta = { value: 0, factor: 0 };
#dispatchCallback;
#drawAnimationFrame;
#duration;
#eventListeners;
#firstStart;
#initialSourceOptions;
#lastFrameTime;
#lifeTime;
#onDestroy;
#options;
#paused;
#pluginManager;
#smooth;
#sourceOptions;
constructor(params) {
const { dispatchCallback, pluginManager, id, onDestroy, sourceOptions } = params;
this.#pluginManager = pluginManager;
this.#dispatchCallback = dispatchCallback;
this.#onDestroy = onDestroy;
this.id = Symbol(id);
this.fpsLimit = 120;
this.hdr = false;
this.hdrMode = exports.HdrMode.standard;
this.peakNits = 400;
this.#smooth = false;
this.#delay = 0;
this.#duration = 0;
this.#lifeTime = 0;
this.#firstStart = true;
this.started = false;
this.destroyed = false;
this.#paused = true;
this.#lastFrameTime = 0;
this.zLayers = 100;
this.pageHidden = false;
this.#sourceOptions = sourceOptions;
this.#initialSourceOptions = sourceOptions;
this.effectDrawers = new Map();
this.shapeDrawers = new Map();
this.particleUpdaters = [];
this.retina = new Retina(this);
this.canvas = new CanvasManager(this.#pluginManager, this);
this.particles = new ParticlesManager(this.#pluginManager, this);
this.plugins = [];
this.particleDestroyedPlugins = [];
this.particleCreatedPlugins = [];
this.particlePositionPlugins = [];
this.#options = loadContainerOptions(this.#pluginManager, this);
this.actualOptions = loadContainerOptions(this.#pluginManager, this);
this.#eventListeners = new EventListeners(this);
this.dispatchEvent(exports.EventType.containerBuilt);
}
get animationStatus() {
return !this.#paused && !this.pageHidden && guardCheck(this);
}
get options() {
return this.#options;
}
get sourceOptions() {
return this.#sourceOptions;
}
addLifeTime(value) {
this.#lifeTime += value;
}
alive() {
return !this.#duration || this.#lifeTime <= this.#duration;
}
destroy(remove = true) {
if (!guardCheck(this)) {
return;
}
this.stop();
this.particles.destroy();
this.canvas.destroy();
for (const [, effectDrawer] of this.effectDrawers) {
effectDrawer.destroy?.(this);
}
for (const [, shapeDrawer] of this.shapeDrawers) {
shapeDrawer.destroy?.(this);
}
for (const plugin of this.plugins) {
plugin.destroy?.();
}
this.effectDrawers = new Map();
this.shapeDrawers = new Map();
this.particleUpdaters = [];
this.plugins.length = 0;
this.#pluginManager.clearPlugins(this);
this.destroyed = true;
this.#onDestroy(remove);
this.dispatchEvent(exports.EventType.containerDestroyed);
}
dispatchEvent(type, data) {
this.#dispatchCallback(type, {
container: this,
data,
});
}
draw(force) {
if (!guardCheck(this)) {
return;
}
let refreshTime = force;
this.#drawAnimationFrame = animate((timestamp) => {
if (refreshTime) {
this.#lastFrameTime = undefined;
refreshTime = false;
}
this.#nextFrame(timestamp);
});
}
async export(type, options = {}) {
for (const plugin of this.plugins) {
if (!plugin.export) {
continue;
}
const res = await plugin.export(type, options);
if (!res.supported) {
continue;
}
return res.blob;
}
getLogger().error(`Export plugin with type ${type} not found`);
return undefined;
}
async init() {
if (!guardCheck(this)) {
return;
}
const allContainerPlugins = new Map();
for (const plugin of this.#pluginManager.plugins) {
const containerPlugin = await plugin.getPlugin(this);
if (containerPlugin.preInit) {
await containerPlugin.preInit();
}
allContainerPlugins.set(plugin, containerPlugin);
}
await this.initDrawersAndUpdaters();
this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
this.plugins.length = 0;
this.particleDestroyedPlugins.length = 0;
this.particleCreatedPlugins.length = 0;
this.particlePositionPlugins.length = 0;
for (const [plugin, containerPlugin] of allContainerPlugins) {
if (plugin.needsPlugin(this.actualOptions)) {
this.plugins.push(containerPlugin);
if (containerPlugin.particleCreated) {
this.particleCreatedPlugins.push(containerPlugin);
}
if (containerPlugin.particleDestroyed) {
this.particleDestroyedPlugins.push(containerPlugin);
}
if (containerPlugin.particlePosition) {
this.particlePositionPlugins.push(containerPlugin);
}
}
}
this.retina.init();
this.canvas.init();
this.updateActualOptions();
this.canvas.initBackground();
this.canvas.resize();
const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
this.hdrMode = hdrOptions.mode;
this.peakNits = hdrOptions.peakNits;
this.zLayers = zLayers;
this.#duration = getRangeValue(duration) * millisecondsToSeconds$1;
this.#delay = getRangeValue(delay) * millisecondsToSeconds$1;
this.#lifeTime = 0;
this.fpsLimit = fpsLimit > minFpsLimit ? fpsLimit : defaultFpsLimit;
this.#smooth = smooth;
for (const plugin of this.plugins) {
await plugin.init?.();
}
await this.particles.init();
this.dispatchEvent(exports.EventType.containerInit);
this.particles.setDensity();
for (const plugin of this.plugins) {
plugin.particlesSetup?.();
}
this.dispatchEvent(exports.EventType.particlesSetup);
}
async initDrawersAndUpdaters() {
const pluginManager = this.#pluginManager;
this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
this.particleUpdaters = await pluginManager.getUpdaters(this, true);
}
pause() {
if (!guardCheck(this)) {
return;
}
if (this.#drawAnimationFrame !== undefined) {
cancelAnimation(this.#drawAnimationFrame);
this.#drawAnimationFrame = undefined;
}
if (this.#paused) {
return;
}
for (const plugin of this.plugins) {
plugin.pause?.();
}
if (!this.pageHidden) {
this.#paused = true;
}
this.dispatchEvent(exports.EventType.containerPaused);
}
play(force) {
if (!guardCheck(this)) {
return;
}
const needsUpdate = this.#paused || force;
if (this.#firstStart && !this.actualOptions.autoPlay) {
this.#firstStart = false;
return;
}
if (this.#paused) {
this.#paused = false;
}
if (needsUpdate) {
for (const plugin of this.plugins) {
if (plugin.play) {
plugin.play();
}
}
}
this.dispatchEvent(exports.EventType.containerPlay);
this.draw(needsUpdate ?? false);
}
async refresh() {
if (!guardCheck(this)) {
return;
}
this.stop();
return this.start();
}
async reset(sourceOptions) {
if (!guardCheck(this)) {
return;
}
this.#initialSourceOptions = sourceOptions;
this.#sourceOptions = sourceOptions;
this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
return this.refresh();
}
async start() {
if (!guardCheck(this) || this.started) {
return;
}
await this.init();
this.started = true;
await new Promise(resolve => {
const start = async () => {
this.#eventListeners.addListeners();
for (const plugin of this.plugins) {
await plugin.start?.();
}
this.dispatchEvent(exports.EventType.containerStarted);
this.play();
resolve();
};
this.#delayTimeout = setTimeout(() => void start(), this.#delay);
});
}
stop() {
if (!guardCheck(this) || !this.started) {
return;
}
if (this.#delayTimeout) {
clearTimeout(this.#delayTimeout);
this.#delayTimeout = undefined;
}
this.#firstStart = true;
this.started = false;
this.#eventListeners.removeListeners();
this.pause();
this.particles.clear();
this.canvas.stop();
for (const plugin of this.plugins) {
plugin.stop?.();
}
this.particleCreatedPlugins.length = 0;
this.particleDestroyedPlugins.length = 0;
this.particlePositionPlugins.length = 0;
this.#sourceOptions = this.#options;
this.dispatchEvent(exports.EventType.containerStopped);
}
updateActualOptions() {
let refresh = false;
for (const plugin of this.plugins) {
if (plugin.updateActualOptions) {
refresh = plugin.updateActualOptions() || refresh;
}
}
return refresh;
}
#nextFrame(timestamp) {
try {
if (!this.#smooth &&
this.#lastFrameTime !== undefined &&
timestamp < this.#lastFrameTime + millisecondsToSeconds$1 / this.fpsLimit) {
this.draw(false);
return;
}
this.#lastFrameTime ??= timestamp;
updateDelta(this.#delta, timestamp - this.#lastFrameTime, this.fpsLimit, this.#smooth);
this.addLifeTime(this.#delta.value);
this.#lastFrameTime = timestamp;
if (this.#delta.value > millisecondsToSeconds$1) {
this.draw(false);
return;
}
this.canvas.render.drawParticles(this.#delta);
if (!this.alive()) {
this.destroy();
return;
}
if (this.animationStatus) {
this.draw(false);
}
}
catch (e) {
getLogger().error("error in animation loop", e);
}
}
}
var Container$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
Container: Container
});
class BackgroundMaskPluginInstance {
layer = exports.DrawLayer.BackgroundMask;
#container;
#coverColorStyle;
#coverImage;
#defaultCompositeValue;
#maskElement;
#maskWarnings = new Set();
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
canvasClear() {
const backgroundMask = this.#container.actualOptions.backgroundMask;
if (!backgroundMask?.enable) {
return false;
}
return this.canvasPaint();
}
canvasPaint() {
const { backgroundMask } = this.#container.actualOptions;
if (!backgroundMask?.enable) {
return false;
}
const canvas = this.#container.canvas;
canvas.render.canvasClear();
const cover = backgroundMask.cover;
let dynamicUsed = false;
const maskElement = this.#maskElement;
if (maskElement) {
dynamicUsed = true;
canvas.render.draw(ctx => {
try {
ctx.drawImage(maskElement, originPoint.x, originPoint.y, canvas.size.width, canvas.size.height);
}
catch {
this.#maskWarnOnce("mask-element-draw-error", "Error drawing background mask cover element onto canvas");
}
});
}
if (cover.draw) {
dynamicUsed = true;
const drawFn = cover.draw;
canvas.render.draw(ctx => {
try {
drawFn(ctx, { value: 0, factor: 1 });
}
catch {
this.#maskWarnOnce("mask-draw-error", "Error in mask cover.draw callback");
}
});
}
if (!dynamicUsed) {
if (this.#coverImage) {
canvas.render.paintImage(this.#coverImage.image, this.#coverImage.opacity);
}
else {
canvas.render.paintBase(this.#coverColorStyle);
}
}
return true;
}
destroy() {
this.#maskElement = null;
this.#maskWarnings.clear();
}
drawSettingsCleanup(context) {
if (!this.#defaultCompositeValue) {
return;
}
context.globalCompositeOperation = this.#defaultCompositeValue;
}
drawSettingsSetup(context) {
const previousComposite = context.globalCompositeOperation, backgroundMask = this.#container.actualOptions.backgroundMask;
this.#defaultCompositeValue = previousComposite;
context.globalCompositeOperation = backgroundMask?.enable ? backgroundMask.composite : previousComposite;
}
async init() {
this.#maskWarnings.clear();
await this.#initCover();
this.#resolveMaskElement();
}
#initCover = async () => {
const options = this.#container.actualOptions, cover = options.backgroundMask?.cover, color = cover?.color;
if (color) {
const coverRgb = rangeColorToRgb(this.#pluginManager, color);
if (coverRgb) {
const coverColor = {
...coverRgb,
a: cover.opacity,
};
this.#coverColorStyle = getStyleFromRgb(coverColor, this.#container.hdr, coverColor.a, this.#container.peakNits, this.#container.hdrMode);
}
}
else if (cover?.image) {
const coverImage = cover.image;
await new Promise((resolve, reject) => {
const img = safeDocument().createElement("img");
img.addEventListener("load", () => {
this.#coverImage = {
image: img,
opacity: cover.opacity,
};
resolve();
});
img.addEventListener("error", () => {
reject(new Error("Error loading image"));
});
img.src = coverImage;
});
}
};
#maskWarnOnce = (key, message) => {
if (this.#maskWarnings.has(key)) {
return;
}
this.#maskWarnings.add(key);
getLogger().warning(`[tsParticles BackgroundMask] ${message}`);
};
#resolveMaskElement = () => {
const cover = this.#container.actualOptions.backgroundMask?.cover;
this.#maskElement = null;
if (!cover?.element) {
return;
}
if (typeof cover.element === "string") {
if (typeof document !== "undefined") {
const node = document.querySelector(cover.element);
if (node instanceof HTMLCanvasElement || node instanceof HTMLVideoElement || node instanceof HTMLImageElement) {
this.#maskElement = node;
}
else if (node) {
this.#maskWarnOnce("mask-element-not-supported", `Mask cover element "${cover.element}" matched a non-drawable element (expected canvas, video, or img)`);
}
else {
this.#maskWarnOnce("mask-element-not-found", `Mask cover element selector "${cover.element}" not found in the DOM`);
}
}
}
else if (cover.element instanceof HTMLCanvasElement ||
cover.element instanceof OffscreenCanvas ||
cover.element instanceof HTMLVideoElement ||
cover.element instanceof HTMLImageElement) {
this.#maskElement = cover.element;
}
};
}
var BackgroundMaskPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
BackgroundMaskPluginInstance: BackgroundMaskPluginInstance
});
const moveSpeedFactor = 60, minSpinRadius = 0, spinFactor = 0.01, defaultPathDelay = 0, noDecay = 1;
function applyDistance(particle) {
const initialPosition = particle.initialPosition, { dx, dy } = getDistances(initialPosition, particle.position), dxFixed = Math.abs(dx), dyFixed = Math.abs(dy), { maxDistance } = particle.retina, hDistance = maxDistance.horizontal, vDistance = maxDistance.vertical;
if (!hDistance && !vDistance) {
return;
}
const hasHDistance = (hDistance && dxFixed >= hDistance) ?? false, hasVDistance = (vDistance && dyFixed >= vDistance) ?? false;
if ((hasHDistance || hasVDistance) && !particle.misplaced) {
particle.misplaced = (!!hDistance && dxFixed > hDistance) || (!!vDistance && dyFixed > vDistance);
if (hDistance) {
particle.velocity.x = particle.velocity.y * half$2 - particle.velocity.x;
}
if (vDistance) {
particle.velocity.y = particle.velocity.x * half$2 - particle.velocity.y;
}
}
else if ((!hDistance || dxFixed < hDistance) && (!vDistance || dyFixed < vDistance) && particle.misplaced) {
particle.misplaced = false;
}
else if (particle.misplaced) {
const pos = particle.position, vel = particle.velocity;
if (hDistance &&
((pos.x < initialPosition.x && vel.x < minVelocity) || (pos.x > initialPosition.x && vel.x > minVelocity))) {
vel.x *= -getRandom();
}
if (vDistance &&
((pos.y < initialPosition.y && vel.y < minVelocity) || (pos.y > initialPosition.y && vel.y > minVelocity))) {
vel.y *= -getRandom();
}
}
}
function move(particle, moveOptions, moveSpeed, maxSpeed, moveDrift, reduceFactor, delta) {
applyPath(particle, delta);
const gravityOptions = particle.gravity, gravityFactor = gravityOptions?.enable && gravityOptions.inverse ? -identity$3 : identity$3;
if (moveDrift && moveSpeed) {
particle.velocity.x += (moveDrift * delta.factor) / (moveSpeedFactor * moveSpeed);
}
if (gravityOptions?.enable && moveSpeed) {
particle.velocity.y +=
(gravityFactor * (gravityOptions.acceleration * delta.factor)) / (moveSpeedFactor * moveSpeed);
}
const decay = particle.moveDecay;
particle.velocity.multTo(decay ?? noDecay);
const velocity = particle.velocity.mult(moveSpeed);
if (gravityOptions?.enable &&
maxSpeed > minVelocity &&
((!gravityOptions.inverse && velocity.y >= minVelocity && velocity.y >= maxSpeed) ||
(gravityOptions.inverse && velocity.y <= minVelocity && velocity.y <= -maxSpeed))) {
velocity.y = gravityFactor * maxSpeed;
if (moveSpeed) {
particle.velocity.y = velocity.y / moveSpeed;
}
}
const zIndexOptions = particle.options.zIndex, zVelocityFactor = (identity$3 - particle.zIndexFactor) ** zIndexOptions.velocityRate;
velocity.multTo(zVelocityFactor);
velocity.multTo(reduceFactor);
const { position } = particle;
position.addTo(velocity);
if (moveOptions.vibrate) {
position.x += Math.sin(position.x * Math.cos(position.y)) * reduceFactor;
position.y += Math.cos(position.y * Math.sin(position.x)) * reduceFactor;
}
}
function spin(container, particle, moveSpeed, reduceFactor) {
if (!particle.spin) {
return;
}
const spinClockwise = particle.spin.direction === exports.RotateDirection.clockwise, updateFunc = {
x: spinClockwise ? Math.cos : Math.sin,
y: spinClockwise ? Math.sin : Math.cos,
};
particle.position.x =
particle.spin.center.x + particle.spin.radius * updateFunc.x(particle.spin.angle) * reduceFactor;
particle.position.y =
particle.spin.center.y + particle.spin.radius * updateFunc.y(particle.spin.angle) * reduceFactor;
particle.spin.radius += particle.spin.acceleration * reduceFactor;
const maxCanvasSize = Math.max(container.canvas.size.width, container.canvas.size.height), halfMaxSize = maxCanvasSize * half$2;
if (particle.spin.radius > halfMaxSize) {
particle.spin.radius = halfMaxSize;
particle.spin.acceleration *= -identity$3;
}
else if (particle.spin.radius < minSpinRadius) {
particle.spin.radius = minSpinRadius;
particle.spin.acceleration *= -identity$3;
}
particle.spin.angle += moveSpeed * spinFactor * (identity$3 - particle.spin.radius / maxCanvasSize);
}
function applyPath(particle, delta) {
const particlesOptions = particle.options, pathOptions = particlesOptions.move.path, pathEnabled = pathOptions.enable;
if (!pathEnabled) {
return;
}
const pathDelay = particle.pathDelay ?? defaultPathDelay;
if (particle.lastPathTime <= pathDelay) {
particle.lastPathTime += delta.value;
return;
}
const path = particle.pathGenerator?.generate(particle, delta);
if (path) {
particle.velocity.addTo(path);
}
if (pathOptions.clamp) {
particle.velocity.x = clamp(particle.velocity.x, -identity$3, identity$3);
particle.velocity.y = clamp(particle.velocity.y, -identity$3, identity$3);
}
particle.lastPathTime -= pathDelay;
}
function getProximitySpeedFactor(particle) {
const mod = particle.getModifier("slow");
return mod?.enabled ? (mod.speedFactor ?? identity$3) : identity$3;
}
function initSpin(container, particle) {
const options = particle.options, spinOptions = options.move.spin;
if (!spinOptions.enable) {
return;
}
const spinPos = spinOptions.position ?? { x: 50, y: 50 }, spinFactor = 0.01, spinCenter = {
x: spinPos.x * spinFactor * container.canvas.size.width,
y: spinPos.y * spinFactor * container.canvas.size.height,
}, pos = particle.getPosition(), distance = getDistance(pos, spinCenter), spinAcceleration = getRangeValue(spinOptions.acceleration);
particle.retina.spinAcceleration = spinAcceleration * container.retina.pixelRatio;
particle.spin = {
center: spinCenter,
direction: particle.velocity.x >= minVelocity ? exports.RotateDirection.clockwise : exports.RotateDirection.counterClockwise,
angle: getRandom() * doublePI,
radius: distance,
acceleration: particle.retina.spinAcceleration,
};
}
const defaultSizeFactor = 1, defaultDeltaFactor = 1;
class MovePluginInstance {
availablePathGenerators;
pathGenerators;
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.availablePathGenerators = new Map();
this.pathGenerators = new Map();
}
destroy() {
this.availablePathGenerators = new Map();
this.pathGenerators = new Map();
}
isEnabled(particle) {
return !particle.destroyed && particle.options.move.enable;
}
particleCreated(particle) {
const options = particle.options, moveOptions = options.move, gravityOptions = moveOptions.gravity, pathOptions = moveOptions.path;
particle.moveDecay = decayOffset - getRangeValue(moveOptions.decay);
particle.pathDelay = getRangeValue(pathOptions.delay.value) * millisecondsToSeconds$1;
if (pathOptions.generator) {
let pathGenerator = this.pathGenerators.get(pathOptions.generator);
if (!pathGenerator) {
pathGenerator = this.availablePathGenerators.get(pathOptions.generator);
if (pathGenerator) {
this.pathGenerators.set(pathOptions.generator, pathGenerator);
pathGenerator.init();
}
}
particle.pathGenerator = pathGenerator;
}
particle.gravity = {
enable: gravityOptions.enable,
acceleration: getRangeValue(gravityOptions.acceleration),
inverse: gravityOptions.inverse,
};
initSpin(this.#container, particle);
}
particleDestroyed(particle) {
const pathGenerator = particle.pathGenerator;
pathGenerator?.reset(particle);
}
particleUpdate(particle, delta) {
const particleOptions = particle.options, moveOptions = particleOptions.move;
if (!moveOptions.enable) {
return;
}
const container = this.#container, slowFactor = getProximitySpeedFactor(particle), reduceFactor = container.retina.reduceFactor, baseSpeed = particle.retina.moveSpeed, moveDrift = particle.retina.moveDrift, maxSize = particle.size.max, sizeFactor = moveOptions.size ? particle.getRadius() / maxSize : defaultSizeFactor, deltaFactor = delta.factor || defaultDeltaFactor, moveSpeed = baseSpeed * sizeFactor * slowFactor * deltaFactor * half$2, maxSpeed = particle.retina.maxSpeed;
if (moveOptions.spin.enable) {
spin(container, particle, moveSpeed, reduceFactor);
}
else {
move(particle, moveOptions, moveSpeed, maxSpeed, moveDrift, reduceFactor, delta);
}
applyDistance(particle);
}
preInit() {
return this.#init();
}
redrawInit() {
return this.#init();
}
update() {
for (const pathGenerator of this.pathGenerators.values()) {
pathGenerator.update();
}
}
async #init() {
const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
if (!availablePathGenerators) {
return;
}
this.availablePathGenerators = availablePathGenerators;
this.pathGenerators = new Map();
for (const pathGenerator of this.pathGenerators.values()) {
pathGenerator.init();
}
}
}
var MovePluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
MovePluginInstance: MovePluginInstance
});
function addParticlesFromCanvasPixels(container, data, position, scale, override, filter) {
const { height, width } = data, numPixels = height * width, indexArray = shuffle(range(numPixels)), maxParticles = Math.min(numPixels, container.actualOptions.particles.number.value), canvasSize = container.canvas.size;
let selectedPixels = 0;
const positionOffset = {
x: (canvasSize.width * position.x) / percentDenominator - width * scale * half$2,
y: (canvasSize.height * position.y) / percentDenominator - height * scale * half$2,
};
while (selectedPixels < maxParticles && indexArray.length) {
const defaultIndex = 0, nextIndex = indexArray.pop() ?? defaultIndex, pixelPos = {
x: nextIndex % width,
y: Math.floor(nextIndex / width),
}, row = data.pixels[pixelPos.y];
if (!row) {
continue;
}
const pixel = row[pixelPos.x];
if (!pixel) {
continue;
}
const shouldCreateParticle = filter(pixel);
if (!shouldCreateParticle) {
continue;
}
const pos = {
x: pixelPos.x * scale + positionOffset.x,
y: pixelPos.y * scale + positionOffset.y,
}, pOptions = {};
if (override.color) {
pOptions.paint = {
fill: {
color: {
value: pixel,
},
enable: true,
},
};
}
if (override.opacity) {
pOptions["opacity"] = {
value: pixel.a,
};
}
container.particles.addParticle(pos, pOptions);
selectedPixels++;
}
}
function shuffle(array) {
const lengthOffset = 1, minIndex = 0;
for (let currentIndex = array.length - lengthOffset; currentIndex >= minIndex; currentIndex--) {
const randomIndex = Math.floor(getRandom() * currentIndex), currentItem = array[currentIndex], randomItem = array[randomIndex];
if (randomItem === currentItem) {
continue;
}
if (randomItem === undefined || currentItem === undefined) {
continue;
}
array[currentIndex] = randomItem;
array[randomIndex] = currentItem;
}
return array;
}
const range = (n) => [...Array(n).keys()];
class CanvasMaskPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
async init() {
const container = this.#container, options = container.actualOptions.canvasMask;
if (!options?.enable) {
return;
}
let pixelData = {
pixels: [],
height: 0,
width: 0,
};
const offset = options.pixels.offset;
if (options.image) {
const url = options.image.src;
if (!url) {
return;
}
pixelData = await getImageData(url, offset, container.canvas.render.settings);
}
else if (options.text) {
const textOptions = options.text, data = getTextData(textOptions, offset, textOptions.fill, container.canvas.render.settings);
if (isNull(data)) {
return;
}
pixelData = data;
}
else if (options.element ?? options.selector) {
const canvas = options.element ?? (options.selector && safeDocument().querySelector(options.selector));
if (!canvas) {
return;
}
const context = canvas.getContext("2d", container.canvas.render.settings);
if (!context) {
return;
}
pixelData = getCanvasImageData(context, canvas, offset);
}
addParticlesFromCanvasPixels(container, pixelData, options.position, options.scale, options.override, options.pixels.filter);
}
}
var CanvasMaskPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
CanvasMaskPluginInstance: CanvasMaskPluginInstance
});
const touchDelay = 500;
class InteractivityEventListeners {
#canPush = true;
#clickPositionPlugins;
#container;
#handlers;
#interactionManager;
#touches;
constructor(container, interactionManager) {
this.#container = container;
this.#clickPositionPlugins = [];
this.#interactionManager = interactionManager;
this.#touches = new Map();
this.#handlers = {
mouseDown: () => {
this.#mouseDown();
},
mouseLeave: () => {
this.#mouseTouchFinish();
},
mouseMove: (e) => {
this.#mouseTouchMove(e);
},
mouseUp: (e) => {
this.#mouseTouchClick(e);
},
touchStart: (e) => {
this.#touchStart(e);
},
touchMove: (e) => {
this.#mouseTouchMove(e);
},
touchEnd: (e) => {
this.#touchEnd(e);
},
touchCancel: (e) => {
this.#touchEnd(e);
},
touchEndClick: (e) => {
this.#touchEndClick(e);
},
visibilityChange: () => {
this.#handleVisibilityChange();
},
};
}
addListeners() {
this.#manageListeners(true);
}
init() {
this.#clickPositionPlugins.length = 0;
for (const plugin of this.#container.plugins.filter(p => !!p.clickPositionValid)) {
this.#clickPositionPlugins.push(plugin);
}
}
removeListeners() {
this.#manageListeners(false);
}
#doMouseTouchClick(e) {
const container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions;
if (this.#canPush) {
const mouseInteractivity = interactionManager.interactivityData.mouse, mousePos = mouseInteractivity.position;
if (!mousePos) {
return;
}
mouseInteractivity.clickPosition = { ...mousePos };
mouseInteractivity.clickTime = performance.now();
const onClick = options.interactivity?.events.onClick;
if (!onClick?.mode) {
return;
}
executeOnSingleOrMultiple(onClick.mode, mode => {
interactionManager.handleClickMode(mode);
});
}
if (e.type === "touchend") {
setTimeout(() => {
this.#mouseTouchFinish();
}, touchDelay);
}
}
#handleVisibilityChange() {
this.#mouseTouchFinish();
}
#manageInteractivityListeners(add) {
const handlers = this.#handlers, container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions, interactivityEl = interactionManager.interactivityData.element;
if (!interactivityEl) {
return;
}
const html = interactivityEl, canvas = container.canvas;
canvas.setPointerEvents(html === canvas.domElement ? "initial" : "none");
if (add && !(options.interactivity?.events.onHover.enable || options.interactivity?.events.onClick.enable)) {
return;
}
manageListener(interactivityEl, mouseMoveEvent, handlers.mouseMove, add);
manageListener(interactivityEl, touchStartEvent$1, handlers.touchStart, add);
manageListener(interactivityEl, touchMoveEvent, handlers.touchMove, add);
if (add) {
if (options.interactivity?.events.onClick.enable) {
manageListener(interactivityEl, touchEndEvent, handlers.touchEndClick, add);
manageListener(interactivityEl, mouseUpEvent, handlers.mouseUp, add);
manageListener(interactivityEl, mouseDownEvent$1, handlers.mouseDown, add);
}
else {
manageListener(interactivityEl, touchEndEvent, handlers.touchEnd, add);
}
}
else {
manageListener(interactivityEl, touchEndEvent, handlers.touchEndClick, add);
manageListener(interactivityEl, mouseUpEvent, handlers.mouseUp, add);
manageListener(interactivityEl, mouseDownEvent$1, handlers.mouseDown, add);
manageListener(interactivityEl, touchEndEvent, handlers.touchEnd, add);
}
manageListener(interactivityEl, mouseLeaveEvent, handlers.mouseLeave, add);
manageListener(interactivityEl, touchCancelEvent, handlers.touchCancel, add);
}
#manageListeners(add) {
const handlers = this.#handlers, container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions, detectType = options.interactivity?.detectsOn, canvasEl = container.canvas.domElement;
if (detectType === InteractivityDetect.window) {
interactionManager.interactivityData.element = safeDocument();
}
else if (detectType === InteractivityDetect.parent && canvasEl) {
interactionManager.interactivityData.element = canvasEl.parentElement ?? canvasEl.parentNode;
}
else {
interactionManager.interactivityData.element = canvasEl;
}
this.#manageInteractivityListeners(add);
manageListener(document, visibilityChangeEvent, handlers.visibilityChange, add, false);
}
#mouseDown() {
const { interactivityData } = this.#interactionManager, { mouse } = interactivityData;
mouse.clicking = true;
mouse.downPosition = mouse.position;
}
#mouseTouchClick(e) {
const container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions, { mouse } = interactionManager.interactivityData;
mouse.inside = true;
let handled = false;
const mousePosition = mouse.position;
if (!mousePosition || !options.interactivity?.events.onClick.enable) {
return;
}
for (const plugin of this.#clickPositionPlugins) {
handled = plugin.clickPositionValid?.(mousePosition) ?? false;
if (handled) {
break;
}
}
if (!handled) {
this.#doMouseTouchClick(e);
}
mouse.clicking = false;
}
#mouseTouchFinish() {
const { interactivityData } = this.#interactionManager, { mouse } = interactivityData;
delete mouse.position;
delete mouse.clickPosition;
delete mouse.downPosition;
interactivityData.status = mouseLeaveEvent;
mouse.inside = false;
mouse.clicking = false;
}
#mouseTouchMove(e) {
const container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions, interactivity = interactionManager.interactivityData, canvasEl = container.canvas.domElement;
if (!interactivity.element) {
return;
}
interactivity.mouse.inside = true;
let pos;
if (e.type.startsWith("pointer")) {
this.#canPush = true;
const mouseEvent = e;
if (interactivity.element === safeDocument()) {
if (canvasEl) {
const clientRect = canvasEl.getBoundingClientRect();
pos = {
x: mouseEvent.clientX - clientRect.left,
y: mouseEvent.clientY - clientRect.top,
};
}
}
else if (options.interactivity?.detectsOn === InteractivityDetect.parent) {
const source = mouseEvent.target, target = mouseEvent.currentTarget;
if (canvasEl) {
const sourceRect = source.getBoundingClientRect(), targetRect = target.getBoundingClientRect(), canvasRect = canvasEl.getBoundingClientRect();
pos = {
x: mouseEvent.offsetX + double * sourceRect.left - (targetRect.left + canvasRect.left),
y: mouseEvent.offsetY + double * sourceRect.top - (targetRect.top + canvasRect.top),
};
}
else {
pos = {
x: mouseEvent.offsetX,
y: mouseEvent.offsetY,
};
}
}
else if (mouseEvent.target === canvasEl) {
pos = {
x: mouseEvent.offsetX,
y: mouseEvent.offsetY,
};
}
}
else {
this.#canPush = e.type !== "touchmove";
if (canvasEl) {
const touchEvent = e, lastTouch = touchEvent.touches[touchEvent.touches.length - lengthOffset], canvasRect = canvasEl.getBoundingClientRect();
if (!lastTouch) {
return;
}
pos = {
x: lastTouch.clientX - canvasRect.left,
y: lastTouch.clientY - canvasRect.top,
};
}
}
const pxRatio = container.retina.pixelRatio;
if (pos) {
pos.x *= pxRatio;
pos.y *= pxRatio;
}
interactivity.mouse.position = pos;
interactivity.status = mouseMoveEvent;
}
#touchEnd(e) {
const evt = e, touches = Array.from(evt.changedTouches);
for (const touch of touches) {
this.#touches.delete(touch.identifier);
}
this.#mouseTouchFinish();
}
#touchEndClick(e) {
const evt = e, touches = Array.from(evt.changedTouches);
for (const touch of touches) {
this.#touches.delete(touch.identifier);
}
this.#mouseTouchClick(e);
}
#touchStart(e) {
const evt = e, touches = Array.from(evt.changedTouches);
for (const touch of touches) {
this.#touches.set(touch.identifier, performance.now());
}
this.#mouseTouchMove(e);
}
}
const clickRadius = 1, touchEndLengthOffset = 1, minCoordinate = 0;
function safeIntersectionObserver(callback) {
if (typeof IntersectionObserver === "undefined") {
return;
}
return new IntersectionObserver(callback);
}
class InteractionManager {
interactivityData;
#clickHandlers;
#container;
#eventListeners;
#externalInteractors;
#interactors;
#intersectionObserver;
#particleInteractors;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
this.#interactors = [];
this.#externalInteractors = [];
this.#particleInteractors = [];
this.#clickHandlers = new Map();
this.#eventListeners = new InteractivityEventListeners(container, this);
this.interactivityData = {
mouse: {
clicking: false,
inside: false,
},
};
this.#intersectionObserver = safeIntersectionObserver(entries => {
this.#intersectionManager(entries);
});
}
addClickHandler(callback) {
const container = this.#container, interactivityData = this.interactivityData;
if (container.destroyed) {
return;
}
const el = interactivityData.element;
if (!el) {
return;
}
const clickOrTouchHandler = (e, pos, radius) => {
if (container.destroyed) {
return;
}
const pxRatio = container.retina.pixelRatio, posRetina = {
x: pos.x * pxRatio,
y: pos.y * pxRatio,
}, particles = container.particles.grid.queryCircle(posRetina, radius * pxRatio);
callback(e, particles);
}, clickHandler = (e) => {
if (container.destroyed) {
return;
}
const mouseEvent = e, pos = {
x: mouseEvent.offsetX,
y: mouseEvent.offsetY,
};
clickOrTouchHandler(e, pos, clickRadius);
}, touchStartHandler = () => {
if (container.destroyed) {
return;
}
touched = true;
touchMoved = false;
}, touchMoveHandler = () => {
if (container.destroyed) {
return;
}
touchMoved = true;
}, touchEndHandler = (e) => {
if (container.destroyed) {
return;
}
if (touched && !touchMoved) {
const touchEvent = e, lastTouch = touchEvent.touches[touchEvent.touches.length - touchEndLengthOffset];
if (!lastTouch) {
return;
}
const element = container.canvas.domElement, canvasRect = element ? element.getBoundingClientRect() : undefined, pos = {
x: lastTouch.clientX - (canvasRect ? canvasRect.left : minCoordinate),
y: lastTouch.clientY - (canvasRect ? canvasRect.top : minCoordinate),
};
clickOrTouchHandler(e, pos, Math.max(lastTouch.radiusX, lastTouch.radiusY));
}
touched = false;
touchMoved = false;
}, touchCancelHandler = () => {
if (container.destroyed) {
return;
}
touched = false;
touchMoved = false;
};
let touched = false, touchMoved = false;
this.#clickHandlers.set(clickEvent, clickHandler);
this.#clickHandlers.set(touchStartEvent$1, touchStartHandler);
this.#clickHandlers.set(touchMoveEvent, touchMoveHandler);
this.#clickHandlers.set(touchEndEvent, touchEndHandler);
this.#clickHandlers.set(touchCancelEvent, touchCancelHandler);
for (const [key, handler] of this.#clickHandlers) {
el.addEventListener(key, handler);
}
}
addListeners() {
this.#eventListeners.addListeners();
}
clearClickHandlers() {
const container = this.#container, interactivityData = this.interactivityData;
if (container.destroyed) {
return;
}
for (const [key, handler] of this.#clickHandlers) {
interactivityData.element?.removeEventListener(key, handler);
}
this.#clickHandlers.clear();
}
externalInteract(delta) {
for (const interactor of this.#externalInteractors) {
const interactivityData = this.interactivityData;
if (interactor.isEnabled(interactivityData)) {
interactor.interact(interactivityData, delta);
}
}
}
handleClickMode(mode) {
if (this.#container.destroyed) {
return;
}
const interactivityData = this.interactivityData;
for (const interactor of this.#externalInteractors) {
interactor.handleClickMode?.(mode, interactivityData);
}
}
init() {
this.#eventListeners.init();
for (const interactor of this.#interactors) {
switch (interactor.type) {
case InteractorType.external:
this.#externalInteractors.push(interactor);
break;
case InteractorType.particles:
this.#particleInteractors.push(interactor);
break;
}
interactor.init();
}
}
async initInteractors() {
const interactors = await this.#pluginManager.getInteractors?.(this.#container, true);
if (!interactors) {
return;
}
this.#interactors = interactors;
this.#externalInteractors = [];
this.#particleInteractors = [];
}
particlesInteract(particle, delta) {
const interactivityData = this.interactivityData;
for (const interactor of this.#externalInteractors) {
interactor.clear(particle, delta);
}
for (const interactor of this.#particleInteractors) {
if (interactor.isEnabled(particle, interactivityData)) {
interactor.interact(particle, interactivityData, delta);
}
}
}
removeListeners() {
this.#eventListeners.removeListeners();
}
reset(particle) {
const interactivityData = this.interactivityData;
for (const interactor of this.#externalInteractors) {
if (interactor.isEnabled(interactivityData)) {
interactor.reset(interactivityData, particle);
}
}
for (const interactor of this.#particleInteractors) {
if (interactor.isEnabled(particle, interactivityData)) {
interactor.reset(interactivityData, particle);
}
}
}
startObserving() {
const interactivityData = this.interactivityData;
if (interactivityData.element instanceof HTMLElement && this.#intersectionObserver) {
this.#intersectionObserver.observe(interactivityData.element);
}
}
stopObserving() {
const interactivityData = this.interactivityData;
if (interactivityData.element instanceof HTMLElement && this.#intersectionObserver) {
this.#intersectionObserver.unobserve(interactivityData.element);
}
}
updateMaxDistance() {
let maxTotalDistance = 0;
for (const interactor of this.#interactors) {
if (interactor.maxDistance > maxTotalDistance) {
maxTotalDistance = interactor.maxDistance;
}
}
const container = this.#container;
container.particles.grid.setCellSize(maxTotalDistance * container.retina.pixelRatio);
}
#intersectionManager(entries) {
const container = this.#container;
if (container.destroyed || !container.actualOptions.pauseOnOutsideViewport) {
return;
}
for (const entry of entries) {
if (entry.target !== this.interactivityData.element) {
continue;
}
if (entry.isIntersecting) {
container.play();
}
else {
container.pause();
}
}
}
}
class InteractivityPluginInstance {
interactionManager;
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
this.interactionManager = new InteractionManager(pluginManager, container);
this.#container.addClickHandler = (callback) => {
this.interactionManager.addClickHandler(callback);
};
}
addClickHandler(callback) {
this.interactionManager.addClickHandler(callback);
}
clearClickHandlers() {
this.interactionManager.clearClickHandlers();
}
destroy() {
this.clearClickHandlers();
this.#pluginManager.interactors?.delete(this.#container);
}
particleCreated(particle) {
const interactivityParticle = particle, interactivity = new Interactivity(this.#pluginManager, this.#container);
interactivity.load(this.#container.actualOptions.interactivity);
interactivity.load(interactivityParticle.options.interactivity);
interactivityParticle.interactivity = interactivity;
}
particleReset(particle) {
this.interactionManager.reset(particle);
}
postParticleUpdate(particle, delta) {
this.interactionManager.particlesInteract(particle, delta);
}
postUpdate(delta) {
this.interactionManager.externalInteract(delta);
this.interactionManager.updateMaxDistance();
}
async preInit() {
await this.interactionManager.initInteractors();
this.interactionManager.init();
}
async redrawInit() {
await this.interactionManager.initInteractors();
this.interactionManager.init();
}
start() {
this.interactionManager.addListeners();
this.interactionManager.startObserving();
return Promise.resolve();
}
stop() {
this.interactionManager.removeListeners();
this.interactionManager.stopObserving();
}
}
var InteractivityPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
InteractivityPluginInstance: InteractivityPluginInstance
});
const shapeGenerators = new Map();
class ShapeManager {
addShapeGenerator(name, generator) {
if (!this.getShapeGenerator(name)) {
shapeGenerators.set(name, generator);
}
}
getShapeGenerator(name) {
return shapeGenerators.get(name);
}
getSupportedShapeGenerators() {
return shapeGenerators.keys();
}
}
var ShapeManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ShapeManager: ShapeManager
});
const defaultIndex$1 = 0;
class EmittersInstancesManager {
#containerArrays;
#pluginManager;
constructor(pluginManager) {
this.#containerArrays = new Map();
this.#pluginManager = pluginManager;
}
async addEmitter(container, options, position) {
const emitterOptions = new Emitter();
emitterOptions.load(options);
const { EmitterInstance } = await Promise.resolve().then(function () { return EmitterInstance$1; }), emitter = new EmitterInstance(this.#pluginManager, container, (emitter) => {
this.removeEmitter(container, emitter);
}, emitterOptions, position);
await emitter.init();
this.getArray(container).push(emitter);
return emitter;
}
clear(container) {
this.initContainer(container);
this.#containerArrays.set(container, []);
}
getArray(container) {
this.initContainer(container);
let array = this.#containerArrays.get(container);
if (!array) {
array = [];
this.#containerArrays.set(container, array);
}
return array;
}
initContainer(container) {
if (this.#containerArrays.has(container)) {
return;
}
this.#containerArrays.set(container, []);
container.getEmitter = (idxOrName) => {
const array = this.getArray(container);
return idxOrName === undefined || isNumber(idxOrName)
? array[idxOrName ?? defaultIndex$1]
: array.find(t => t.name === idxOrName);
};
container.addEmitter = async (options, position) => this.addEmitter(container, options, position);
container.removeEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
this.removeEmitter(container, emitter);
}
};
container.playEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
emitter.externalPlay();
}
};
container.pauseEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
emitter.externalPause();
}
};
}
removeEmitter(container, emitter) {
const index = this.getArray(container).indexOf(emitter), minIndex = 0, deleteCount = 1;
if (index >= minIndex) {
this.getArray(container).splice(index, deleteCount);
}
}
}
var EmittersInstancesManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmittersInstancesManager: EmittersInstancesManager
});
class EmittersPluginInstance {
#container;
#instancesManager;
constructor(instancesManager, container) {
this.#instancesManager = instancesManager;
this.#container = container;
this.#instancesManager.initContainer(container);
}
async init() {
const emittersOptions = this.#container.actualOptions.emitters;
if (isArray(emittersOptions)) {
for (const emitterOptions of emittersOptions) {
await this.#instancesManager.addEmitter(this.#container, emitterOptions);
}
}
else {
await this.#instancesManager.addEmitter(this.#container, emittersOptions);
}
}
pause() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.pause();
}
}
play() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.play();
}
}
resize() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.resize();
}
}
stop() {
this.#instancesManager.clear(this.#container);
}
update(delta) {
this.#instancesManager.getArray(this.#container).forEach(emitter => {
emitter.update(delta);
});
}
}
var EmittersPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmittersPluginInstance: EmittersPluginInstance
});
class ExportImagePluginInstance {
#container;
constructor(container) {
this.#container = container;
}
async export(type, data) {
const res = {
supported: false,
};
switch (type) {
case "image":
res.supported = true;
res.blob = await this.#exportImage(data);
break;
}
return res;
}
async #exportImage(data) {
const element = this.#container.canvas.domElement;
if (!element) {
return;
}
return new Promise(resolve => {
element.toBlob(blob => {
if (!blob) {
resolve(undefined);
return;
}
resolve(blob);
}, data.type ?? "image/png", data.quality);
});
}
}
var ExportImagePluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ExportImagePluginInstance: ExportImagePluginInstance
});
const indent = 2;
class ExportJSONPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
async export(type) {
const res = {
supported: false,
};
switch (type) {
case "json":
res.supported = true;
res.blob = await this.#exportJSON();
break;
}
return res;
}
#exportJSON = async () => {
const json = JSON.stringify(this.#container.actualOptions, (key, value) => {
if (key.startsWith("_")) {
return;
}
return value;
}, indent);
return Promise.resolve(new Blob([json], { type: "application/json" }));
};
}
var ExportJSONPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ExportJSONPluginInstance: ExportJSONPluginInstance
});
const videoTypes = ["webm", "ogg", "mp4", "x-matroska"], codecs = [
"vp9",
"vp9.0",
"vp8",
"vp8.0",
"avc1",
"av1",
"h265",
"h.265",
"h264",
"h.264",
"opus",
"pcm",
"aac",
"mpeg",
"mp4a",
];
function getVideoSupportedMimeTypes() {
const isSupported = (type) => MediaRecorder.isTypeSupported(type), supported = [];
videoTypes.forEach(type => {
const mimeType = `video/${type}`;
codecs.forEach(codec => {
[
`${mimeType};codecs=${codec}`,
`${mimeType};codecs=${codec.toUpperCase()}`,
].forEach(variation => {
if (isSupported(variation)) {
supported.push(variation);
}
});
});
if (isSupported(mimeType)) {
supported.push(mimeType);
}
});
return supported;
}
class ExportVideoPluginInstance {
#container;
#supportedTypes = [];
constructor(container) {
this.#container = container;
this.#supportedTypes = getVideoSupportedMimeTypes();
}
async export(type, data) {
const res = {
supported: false,
};
switch (type) {
case "video":
res.supported = true;
res.blob = await this.#exportVideo(data);
break;
}
return res;
}
async #exportVideo(data) {
const element = this.#container.canvas.domElement;
if (!element) {
return;
}
return new Promise(resolve => {
const stream = element.captureStream(data.fps ?? this.#container.actualOptions.fpsLimit), firstIndex = 0, mimeType = data.mimeType ?? this.#supportedTypes[firstIndex], recorder = new MediaRecorder(stream, {
mimeType,
}), chunks = [], defaultDuration = 5;
recorder.addEventListener("dataavailable", (event) => {
chunks.push(event.data);
});
recorder.addEventListener("stop", () => {
resolve(new Blob(chunks, { type: mimeType }));
});
recorder.start();
setTimeout(() => {
recorder.stop();
}, data.duration ?? defaultDuration * millisecondsToSeconds$1);
});
}
}
var ExportVideoPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ExportVideoPluginInstance: ExportVideoPluginInstance
});
const defaultIndex = 0;
class AbsorbersInstancesManager {
#containerArrays;
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
this.#containerArrays = new Map();
}
async addAbsorber(container, options, position) {
const { AbsorberInstance } = await Promise.resolve().then(function () { return AbsorberInstance$1; }), absorber = new AbsorberInstance(this.#pluginManager, container, options, position), array = this.getArray(container);
array.push(absorber);
return absorber;
}
clear(container) {
this.initContainer(container);
this.#containerArrays.set(container, []);
}
getArray(container) {
this.initContainer(container);
let array = this.#containerArrays.get(container);
if (!array) {
array = [];
this.#containerArrays.set(container, array);
}
return array;
}
initContainer(container) {
if (this.#containerArrays.has(container)) {
return;
}
this.#containerArrays.set(container, []);
container.getAbsorber ??= (idxOrName) => {
const array = this.getArray(container);
return idxOrName === undefined || isNumber(idxOrName)
? array[idxOrName ?? defaultIndex]
: array.find(t => t.name === idxOrName);
};
container.addAbsorber ??= (options, position) => {
return this.addAbsorber(container, options, position);
};
}
removeAbsorber(container, absorber) {
const index = this.getArray(container).indexOf(absorber), deleteCount = 1;
if (index >= defaultIndex) {
this.getArray(container).splice(index, deleteCount);
}
}
}
var AbsorbersInstancesManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
AbsorbersInstancesManager: AbsorbersInstancesManager
});
class AbsorbersPluginInstance {
#container;
#instancesManager;
constructor(container, instancesManager) {
this.#container = container;
this.#instancesManager = instancesManager;
this.#instancesManager.initContainer(container);
}
draw(context) {
for (const absorber of this.#instancesManager.getArray(this.#container)) {
absorber.draw(context);
}
}
async init() {
const absorbers = this.#container.actualOptions.absorbers, promises = executeOnSingleOrMultiple(absorbers, async (absorber) => {
await this.#instancesManager.addAbsorber(this.#container, absorber);
});
if (isArray(promises)) {
await Promise.all(promises);
}
else {
await promises;
}
}
particleUpdate(particle, delta) {
for (const absorber of this.#instancesManager.getArray(this.#container)) {
absorber.attract(particle, delta);
if (particle.destroyed) {
break;
}
}
}
resize() {
for (const absorber of this.#instancesManager.getArray(this.#container)) {
absorber.resize();
}
}
stop() {
this.#instancesManager.clear(this.#container);
}
}
var AbsorbersPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
AbsorbersPluginInstance: AbsorbersPluginInstance
});
class BlendPluginInstance {
layer = exports.DrawLayer.CanvasSetup;
#container;
#defaultCompositeValue;
constructor(container) {
this.#container = container;
}
drawParticleCleanup(context, particle) {
if (!particle.options.blend?.enable) {
return;
}
context.globalCompositeOperation = particle.originalBlendMode ?? defaultCompositeValue;
particle.originalBlendMode = undefined;
}
drawParticleSetup(context, particle) {
if (!particle.options.blend?.enable) {
return;
}
particle.originalBlendMode = context.globalCompositeOperation;
context.globalCompositeOperation = particle.options.blend.mode;
}
drawSettingsCleanup(context) {
if (!this.#defaultCompositeValue) {
return;
}
context.globalCompositeOperation = this.#defaultCompositeValue;
}
drawSettingsSetup(context) {
const previousComposite = context.globalCompositeOperation, blend = this.#container.actualOptions.blend;
this.#defaultCompositeValue = previousComposite;
context.globalCompositeOperation = blend?.enable ? blend.mode : previousComposite;
}
}
var BlendPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
BlendPluginInstance: BlendPluginInstance
});
class ImagePreloaderInstance {
#container;
#engine;
constructor(engine, container) {
this.#engine = engine;
this.#container = container;
}
destroy() {
this.#engine.images?.delete(this.#container);
}
}
var ImagePreloaderInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ImagePreloaderInstance: ImagePreloaderInstance
});
const minRetries = 0;
class OverlapPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
checkParticlePosition(particle, position, tryCount) {
return !this.#hasOverlaps(particle, position, tryCount);
}
#hasOverlaps(particle, pos, tryCount) {
const collisionsOptions = particle.options.collisions;
if (!collisionsOptions?.enable) {
return false;
}
const overlapOptions = collisionsOptions.overlap;
if (overlapOptions.enable) {
return false;
}
const retries = overlapOptions.retries;
if (retries >= minRetries && tryCount > retries) {
throw new Error(`Particle is overlapping and can't be placed`);
}
return !!this.#container.particles.find(p => getDistance(pos, p.position) < particle.getRadius() + p.getRadius());
}
}
var OverlapPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
OverlapPluginInstance: OverlapPluginInstance
});
function getLinkKey(ids) {
return [...ids].sort((a, b) => a - b).join("_");
}
function setLinkFrequency(particles, dictionary) {
const key = getLinkKey(particles.map(t => t.id));
let res = dictionary.get(key);
if (res === undefined) {
res = getRandom();
dictionary.set(key, res);
}
return res;
}
const minOpacity = 0, minWidth = 0, minDistance = 0, maxFrequency = 1, defaultFrequency = 0;
class LinkInstance {
#colorCache = new Map();
#container;
#freqs;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#freqs = { links: new Map(), triangles: new Map() };
}
drawParticle(context, particle) {
const { links, options } = particle;
if (!links?.length || !options.links) {
return;
}
const linkOpts = options.links, width = particle.retina.linksWidth ?? minWidth, pos1 = particle.getPosition(), twinkle = particle.options["twinkle"]?.links, trianglesEnabled = linkOpts.triangles.enable, p1Destinations = trianglesEnabled ? new Set(links.map(l => l.destination.id)) : null, originalAlpha = context.globalAlpha;
let currentColorStyle = "", currentWidth = -1, currentAlpha = -1, pathOpen = false;
const flushLines = () => {
if (pathOpen) {
context.stroke();
pathOpen = false;
}
};
for (const link of links) {
if (linkOpts.frequency < maxFrequency &&
this.#getLinkFrequency(particle, link.destination) > linkOpts.frequency) {
continue;
}
const pos2 = link.destination.getPosition();
if (trianglesEnabled && !link.isWarped && p1Destinations) {
flushLines();
this.#drawTriangles(options, particle, link, p1Destinations, pos1, pos2, context);
}
if (link.opacity <= minOpacity || width <= minWidth) {
continue;
}
if (!linkOpts.enable) {
continue;
}
let opacity = link.opacity, colorLine = link.color;
const twinkleRgb = twinkle?.enable && getRandom() < twinkle.frequency
? rangeColorToRgb(this.#pluginManager, twinkle.color)
: undefined;
if (twinkle && twinkleRgb) {
colorLine = twinkleRgb;
opacity = getRangeValue(twinkle.opacity);
}
if (!colorLine) {
const linkColor = linkOpts.id !== undefined
? this.#container.particles.linksColors.get(linkOpts.id)
: this.#container.particles.linksColor;
colorLine = getLinkColor(particle, link.destination, linkColor);
}
if (!colorLine) {
continue;
}
const colorStyle = this.#getCachedStyle(colorLine);
if (colorStyle !== currentColorStyle || width !== currentWidth || opacity !== currentAlpha) {
flushLines();
context.strokeStyle = colorStyle;
context.lineWidth = width;
context.globalAlpha = opacity;
currentColorStyle = colorStyle;
currentWidth = width;
currentAlpha = opacity;
context.beginPath();
pathOpen = true;
}
if (link.isWarped) {
const canvasSize = this.#container.canvas.size, dx = pos2.x - pos1.x, dy = pos2.y - pos1.y;
let sx = originPoint.x, sy = originPoint.y;
if (Math.abs(dx) > canvasSize.width * half$2) {
sx = dx > minDistance ? -canvasSize.width : canvasSize.width;
}
if (Math.abs(dy) > canvasSize.height * half$2) {
sy = dy > minDistance ? -canvasSize.height : canvasSize.height;
}
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x + sx, pos2.y + sy);
context.moveTo(pos1.x - sx, pos1.y - sy);
context.lineTo(pos2.x, pos2.y);
}
else {
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
}
}
flushLines();
context.globalAlpha = originalAlpha;
}
init() {
this.#freqs.links.clear();
this.#freqs.triangles.clear();
this.#colorCache.clear();
return Promise.resolve();
}
particleCreated(particle) {
particle.links = [];
if (!particle.options.links) {
return;
}
particle.linksDistance = particle.options.links.distance;
particle.linksWidth = particle.options.links.width;
const ratio = this.#container.retina.pixelRatio;
particle.retina.linksDistance = particle.linksDistance * ratio;
particle.retina.linksWidth = particle.linksWidth * ratio;
}
particleDestroyed(particle) {
particle.links = [];
}
#drawTriangles(options, p1, link, p1Destinations, pos1, pos2, context) {
const p2 = link.destination, triangleOptions = options.links?.triangles;
if (!triangleOptions?.enable || !p2.options.links?.triangles.enable) {
return;
}
const p2Links = p2.links;
if (!p2Links?.length) {
return;
}
for (const vertex of p2Links) {
if (vertex.isWarped ||
this.#getLinkFrequency(p2, vertex.destination) > p2.options.links.frequency ||
!p1Destinations.has(vertex.destination.id)) {
continue;
}
const p3 = vertex.destination;
if (this.#getTriangleFrequency(p1, p2, p3) > (options.links?.triangles.frequency ?? defaultFrequency)) {
continue;
}
const opacityTriangle = triangleOptions.opacity ?? (link.opacity + vertex.opacity) * half$2, colorTriangle = rangeColorToRgb(this.#pluginManager, triangleOptions.color) ?? link.color;
if (!colorTriangle || opacityTriangle <= minOpacity) {
continue;
}
const pos3 = p3.getPosition();
context.save();
context.fillStyle = this.#getCachedStyle(colorTriangle);
context.globalAlpha = opacityTriangle;
context.beginPath();
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
context.lineTo(pos3.x, pos3.y);
context.closePath();
context.fill();
context.restore();
}
}
#getCachedStyle(rgb) {
const key = `${rgb.r},${rgb.g},${rgb.b}-${this.#container.hdr}-${this.#container.peakNits}-${this.#container.hdrMode}`;
let style = this.#colorCache.get(key);
if (!style) {
style = getStyleFromRgb(rgb, this.#container.hdr, undefined, this.#container.peakNits, this.#container.hdrMode);
this.#colorCache.set(key, style);
}
return style;
}
#getLinkFrequency(p1, p2) {
return setLinkFrequency([p1, p2], this.#freqs.links);
}
#getTriangleFrequency(p1, p2, p3) {
return setLinkFrequency([p1, p2, p3], this.#freqs.triangles);
}
}
var LinkInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
LinkInstance: LinkInstance
});
const minStage$1 = 0, minDuration = 0;
class Infecter {
#container;
constructor(container) {
this.#container = container;
}
startInfection(particle, stage) {
const infectionOptions = this.#container.actualOptions.infection, { infection } = particle;
if (!infectionOptions || !infection) {
return;
}
const stages = infectionOptions.stages, stagesCount = stages.length;
if (stage > stagesCount || stage < minStage$1) {
return;
}
infection.delay = 0;
infection.delayStage = stage;
}
updateInfection(particle, delta) {
const infectionOptions = this.#container.actualOptions.infection, { infection } = particle;
if (!infectionOptions || !infection) {
return;
}
const stages = infectionOptions.stages, stagesCount = stages.length;
if (infection.delay !== undefined && infection.delayStage !== undefined) {
const stage = infection.delayStage;
if (stage > stagesCount || stage < minStage$1) {
return;
}
if (infection.delay >= infectionOptions.delay * millisecondsToSeconds$1) {
infection.stage = stage;
infection.time = 0;
delete infection.delay;
delete infection.delayStage;
}
else {
infection.delay += delta;
}
}
else {
delete infection.delay;
delete infection.delayStage;
}
if (infection.stage !== undefined && infection.time !== undefined) {
const infectionStage = stages[infection.stage];
if (infectionStage?.duration !== undefined && infectionStage.duration >= minDuration) {
if (infection.time > infectionStage.duration * millisecondsToSeconds$1) {
this.#nextInfectionStage(particle);
}
else {
infection.time += delta;
}
}
else {
infection.time += delta;
}
}
else {
delete infection.stage;
delete infection.time;
}
}
updateInfectionStage(particle, stage) {
const options = this.#container.actualOptions, { infection } = particle;
if (!options.infection || !infection) {
return;
}
const stagesCount = options.infection.stages.length;
if (stage > stagesCount || stage < minStage$1 || (infection.stage !== undefined && infection.stage > stage)) {
return;
}
infection.stage = stage;
infection.time = 0;
}
#nextInfectionStage(particle) {
const infectionOptions = this.#container.actualOptions.infection, { infection } = particle;
if (!infectionOptions || !infection) {
return;
}
const stagesCount = infectionOptions.stages.length;
if (stagesCount <= minStage$1 || infection.stage === undefined) {
return;
}
infection.time = 0;
if (stagesCount <= ++infection.stage) {
if (infectionOptions.cure) {
delete infection.stage;
delete infection.time;
}
else {
infection.stage = 0;
infection.time = 0;
}
}
}
}
const minStage = 0;
class InfectionPluginInstance {
#container;
constructor(container) {
this.#container = container;
this.#container.infecter = new Infecter(this.#container);
}
particleFillColor(particle) {
const options = this.#container.actualOptions;
if (!particle.infection || !options.infection) {
return;
}
const infectionStage = particle.infection.stage, infection = options.infection, infectionStages = infection.stages;
return infectionStage === undefined ? undefined : infectionStages[infectionStage]?.color;
}
particleStrokeColor(particle) {
return this.particleFillColor(particle);
}
particlesSetup() {
const options = this.#container.actualOptions;
if (!options.infection) {
return;
}
for (let i = 0; i < options.infection.infections; i++) {
const notInfected = this.#container.particles.filter(p => {
const infP = p;
infP.infection ??= {};
return infP.infection.stage === undefined;
}), infected = itemFromArray(notInfected);
if (!infected) {
continue;
}
this.#container.infecter?.startInfection(infected, minStage);
}
}
}
var InfectionPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
InfectionPluginInstance: InfectionPluginInstance
});
const noParticles = 0;
class ManualParticlesPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
particlesDensityCount() {
return this.#container.actualOptions.manualParticles?.length ?? noParticles;
}
particlesInitialization() {
const container = this.#container, options = container.actualOptions;
options.manualParticles?.forEach(p => container.particles.addParticle(p.position ? getPosition(p.position, container.canvas.size) : undefined, p.options));
return false;
}
}
var ManualParticlesPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ManualParticlesPluginInstance: ManualParticlesPluginInstance
});
const defaultFactor = 1, defaultReduce = 1, disableReduce = 0, identity = 1;
class MotionPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
async init() {
const container = this.#container, options = container.actualOptions.motion;
if (!(options && (options.disable || options.reduce.value))) {
container.retina.reduceFactor = 1;
return;
}
const mediaQuery = safeMatchMedia("(prefers-reduced-motion: reduce)");
if (!mediaQuery) {
container.retina.reduceFactor = defaultFactor;
return;
}
this.#handleMotionChange(mediaQuery);
const handleChange = () => {
void (async () => {
this.#handleMotionChange(mediaQuery);
try {
await container.refresh();
}
catch {
}
})();
};
mediaQuery.addEventListener("change", handleChange);
await Promise.resolve();
}
#handleMotionChange(mediaQuery) {
const container = this.#container, motion = container.actualOptions.motion;
if (!motion) {
return;
}
if (mediaQuery.matches) {
if (motion.disable) {
container.retina.reduceFactor = disableReduce;
}
else {
container.retina.reduceFactor = motion.reduce.value ? identity / motion.reduce.factor : defaultFactor;
}
}
else {
container.retina.reduceFactor = defaultReduce;
}
}
}
var MotionPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
MotionPluginInstance: MotionPluginInstance
});
const defaultSize = 1;
class PoissonDiscPluginInstance {
poissonDisc;
redrawTimeout;
#container;
#currentIndex;
constructor(container) {
this.#container = container;
this.#currentIndex = 0;
}
async init() {
await this.#initData();
}
particlePosition(position) {
const container = this.#container, options = container.actualOptions.poisson;
if (!this.poissonDisc || !(options?.enable ?? false) || this.#currentIndex >= this.poissonDisc.points.length) {
return;
}
return position ?? this.poissonDisc.points[this.#currentIndex++]?.position;
}
resize() {
const container = this.#container, options = container.actualOptions.poisson;
if (!(options?.enable ?? false)) {
return;
}
if (this.redrawTimeout) {
clearTimeout(this.redrawTimeout);
}
const timeout = 250;
this.redrawTimeout = setTimeout(() => {
void (async () => {
if (this.#container.destroyed) {
return;
}
await this.#initData();
await container.particles.redraw();
})();
}, timeout);
}
stop() {
delete this.poissonDisc;
}
async #initData() {
const container = this.#container, poissonOptions = container.actualOptions.poisson, particlesOptions = container.actualOptions.particles, canvasSize = container.canvas.size, pixelRatio = container.retina.pixelRatio;
if (!poissonOptions?.enable) {
return;
}
this.#currentIndex = 0;
const { PoissonDisc } = await Promise.resolve().then(function () { return PoissonDisc$1; });
this.poissonDisc = new PoissonDisc(canvasSize, poissonOptions.radius
? poissonOptions.radius * pixelRatio
: Math.max(getRangeMax(particlesOptions["size.value"] ?? defaultSize) * pixelRatio, Math.sqrt((canvasSize.width * canvasSize.height) / particlesOptions.number.value)), poissonOptions.retries, poissonOptions.dimensions);
const noSteps = 0;
if (poissonOptions.steps > noSteps) {
this.poissonDisc.steps(poissonOptions.steps);
}
else {
await this.poissonDisc.run();
}
}
}
var PoissonDiscPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
PoissonDiscPluginInstance: PoissonDiscPluginInstance
});
(function () {
try {
if (typeof window === "undefined")
return;
if (!("SVGPathSeg" in window)) {
window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) {
this.pathSegType = type;
this.pathSegTypeAsLetter = typeAsLetter;
this._owningPathSegList = owningPathSegList;
};
window.SVGPathSeg.prototype.classname = "SVGPathSeg";
window.SVGPathSeg.PATHSEG_UNKNOWN = 0;
window.SVGPathSeg.PATHSEG_CLOSEPATH = 1;
window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2;
window.SVGPathSeg.PATHSEG_MOVETO_REL = 3;
window.SVGPathSeg.PATHSEG_LINETO_ABS = 4;
window.SVGPathSeg.PATHSEG_LINETO_REL = 5;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9;
window.SVGPathSeg.PATHSEG_ARC_ABS = 10;
window.SVGPathSeg.PATHSEG_ARC_REL = 11;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
window.SVGPathSeg.prototype._segmentChanged = function () {
if (this._owningPathSegList)
this._owningPathSegList.segmentChanged(this);
};
window.SVGPathSegClosePath = function (owningPathSegList) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
};
window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegClosePath.prototype.toString = function () {
return "[object SVGPathSegClosePath]";
};
window.SVGPathSegClosePath.prototype._asPathString = function () {
return this.pathSegTypeAsLetter;
};
window.SVGPathSegClosePath.prototype.clone = function () {
return new window.SVGPathSegClosePath(undefined);
};
window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoAbs.prototype.toString = function () {
return "[object SVGPathSegMovetoAbs]";
};
window.SVGPathSegMovetoAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegMovetoAbs.prototype.clone = function () {
return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoRel.prototype.toString = function () {
return "[object SVGPathSegMovetoRel]";
};
window.SVGPathSegMovetoRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegMovetoRel.prototype.clone = function () {
return new window.SVGPathSegMovetoRel(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoAbs.prototype.toString = function () {
return "[object SVGPathSegLinetoAbs]";
};
window.SVGPathSegLinetoAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegLinetoAbs.prototype.clone = function () {
return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoRel.prototype.toString = function () {
return "[object SVGPathSegLinetoRel]";
};
window.SVGPathSegLinetoRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegLinetoRel.prototype.clone = function () {
return new window.SVGPathSegLinetoRel(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
};
window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () {
return "[object SVGPathSegCurvetoCubicAbs]";
};
window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x1} ${this._y1} ${this._x2} ${this._y2} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () {
return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
};
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", {
get: function () {
return this._x1;
},
set: function (x1) {
this._x1 = x1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", {
get: function () {
return this._y1;
},
set: function (y1) {
this._y1 = y1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", {
get: function () {
return this._x2;
},
set: function (x2) {
this._x2 = x2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", {
get: function () {
return this._y2;
},
set: function (y2) {
this._y2 = y2;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
};
window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicRel.prototype.toString = function () {
return "[object SVGPathSegCurvetoCubicRel]";
};
window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x1} ${this._y1} ${this._x2} ${this._y2} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoCubicRel.prototype.clone = function () {
return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
};
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", {
get: function () {
return this._x1;
},
set: function (x1) {
this._x1 = x1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", {
get: function () {
return this._y1;
},
set: function (y1) {
this._y1 = y1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", {
get: function () {
return this._x2;
},
set: function (x2) {
this._x2 = x2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", {
get: function () {
return this._y2;
},
set: function (y2) {
this._y2 = y2;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
};
window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () {
return "[object SVGPathSegCurvetoQuadraticAbs]";
};
window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x1} ${this._y1} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () {
return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1);
};
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", {
get: function () {
return this._x1;
},
set: function (x1) {
this._x1 = x1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", {
get: function () {
return this._y1;
},
set: function (y1) {
this._y1 = y1;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
};
window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () {
return "[object SVGPathSegCurvetoQuadraticRel]";
};
window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x1} ${this._y1} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () {
return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1);
};
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", {
get: function () {
return this._x1;
},
set: function (x1) {
this._x1 = x1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", {
get: function () {
return this._y1;
},
set: function (y1) {
this._y1 = y1;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
};
window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcAbs.prototype.toString = function () {
return "[object SVGPathSegArcAbs]";
};
window.SVGPathSegArcAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._r1} ${this._r2} ${this._angle} ${this._largeArcFlag ? "1" : "0"} ${this._sweepFlag ? "1" : "0"} ${this._x} ${this._y}`;
};
window.SVGPathSegArcAbs.prototype.clone = function () {
return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
};
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", {
get: function () {
return this._r1;
},
set: function (r1) {
this._r1 = r1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", {
get: function () {
return this._r2;
},
set: function (r2) {
this._r2 = r2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", {
get: function () {
return this._angle;
},
set: function (angle) {
this._angle = angle;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", {
get: function () {
return this._largeArcFlag;
},
set: function (largeArcFlag) {
this._largeArcFlag = largeArcFlag;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", {
get: function () {
return this._sweepFlag;
},
set: function (sweepFlag) {
this._sweepFlag = sweepFlag;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
};
window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcRel.prototype.toString = function () {
return "[object SVGPathSegArcRel]";
};
window.SVGPathSegArcRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._r1} ${this._r2} ${this._angle} ${this._largeArcFlag ? "1" : "0"} ${this._sweepFlag ? "1" : "0"} ${this._x} ${this._y}`;
};
window.SVGPathSegArcRel.prototype.clone = function () {
return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
};
Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", {
get: function () {
return this._r1;
},
set: function (r1) {
this._r1 = r1;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", {
get: function () {
return this._r2;
},
set: function (r2) {
this._r2 = r2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", {
get: function () {
return this._angle;
},
set: function (angle) {
this._angle = angle;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", {
get: function () {
return this._largeArcFlag;
},
set: function (largeArcFlag) {
this._largeArcFlag = largeArcFlag;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", {
get: function () {
return this._sweepFlag;
},
set: function (sweepFlag) {
this._sweepFlag = sweepFlag;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
this._x = x;
};
window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () {
return "[object SVGPathSegLinetoHorizontalAbs]";
};
window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x}`;
};
window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () {
return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x);
};
Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
this._x = x;
};
window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () {
return "[object SVGPathSegLinetoHorizontalRel]";
};
window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x}`;
};
window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () {
return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x);
};
Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
this._y = y;
};
window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () {
return "[object SVGPathSegLinetoVerticalAbs]";
};
window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._y}`;
};
window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () {
return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y);
};
Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
this._y = y;
};
window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalRel.prototype.toString = function () {
return "[object SVGPathSegLinetoVerticalRel]";
};
window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._y}`;
};
window.SVGPathSegLinetoVerticalRel.prototype.clone = function () {
return new window.SVGPathSegLinetoVerticalRel(undefined, this._y);
};
Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
};
window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () {
return "[object SVGPathSegCurvetoCubicSmoothAbs]";
};
window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x2} ${this._y2} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () {
return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2);
};
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", {
get: function () {
return this._x2;
},
set: function (x2) {
this._x2 = x2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", {
get: function () {
return this._y2;
},
set: function (y2) {
this._y2 = y2;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
};
window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () {
return "[object SVGPathSegCurvetoCubicSmoothRel]";
};
window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x2} ${this._y2} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () {
return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2);
};
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", {
get: function () {
return this._x2;
},
set: function (x2) {
this._x2 = x2;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", {
get: function () {
return this._y2;
},
set: function (y2) {
this._y2 = y2;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () {
return "[object SVGPathSegCurvetoQuadraticSmoothAbs]";
};
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () {
return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
this._x = x;
this._y = y;
};
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () {
return "[object SVGPathSegCurvetoQuadraticSmoothRel]";
};
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () {
return `${this.pathSegTypeAsLetter} ${this._x} ${this._y}`;
};
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () {
return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y);
};
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", {
get: function () {
return this._x;
},
set: function (x) {
this._x = x;
this._segmentChanged();
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", {
get: function () {
return this._y;
},
set: function (y) {
this._y = y;
this._segmentChanged();
},
enumerable: true,
});
window.SVGPathElement.prototype.createSVGPathSegClosePath = function () {
return new window.SVGPathSegClosePath(undefined);
};
window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) {
return new window.SVGPathSegMovetoAbs(undefined, x, y);
};
window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) {
return new window.SVGPathSegMovetoRel(undefined, x, y);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) {
return new window.SVGPathSegLinetoAbs(undefined, x, y);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) {
return new window.SVGPathSegLinetoRel(undefined, x, y);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) {
return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) {
return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) {
return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) {
return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1);
};
window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
};
window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) {
return new window.SVGPathSegLinetoHorizontalAbs(undefined, x);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) {
return new window.SVGPathSegLinetoHorizontalRel(undefined, x);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) {
return new window.SVGPathSegLinetoVerticalAbs(undefined, y);
};
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) {
return new window.SVGPathSegLinetoVerticalRel(undefined, y);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) {
return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) {
return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) {
return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y);
};
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) {
return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y);
};
if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) {
window.SVGPathElement.prototype.getPathSegAtLength = function (distance) {
if (distance === undefined || !isFinite(distance))
throw "Invalid arguments.";
const measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
measurementElement.setAttribute("d", this.getAttribute("d"));
let lastPathSegment = measurementElement.pathSegList.numberOfItems - 1;
if (lastPathSegment <= 0)
return 0;
do {
measurementElement.pathSegList.removeItem(lastPathSegment);
if (distance > measurementElement.getTotalLength())
break;
lastPathSegment--;
} while (lastPathSegment > 0);
return lastPathSegment;
};
}
}
if (!("SVGPathSegList" in window) || !("appendItem" in window.SVGPathSegList.prototype)) {
window.SVGPathSegList = function (pathElement) {
this._pathElement = pathElement;
this._list = this._parsePath(this._pathElement.getAttribute("d"));
this._mutationObserverConfig = { attributes: true, attributeFilter: ["d"] };
this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
};
window.SVGPathSegList.prototype.classname = "SVGPathSegList";
Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
get: function () {
this._checkPathSynchronizedToList();
return this._list.length;
},
enumerable: true,
});
Object.defineProperty(window.SVGPathSegList.prototype, "length", {
get: function () {
this._checkPathSynchronizedToList();
return this._list.length;
},
enumerable: true,
});
Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", {
get: function () {
if (!this._pathSegList)
this._pathSegList = new window.SVGPathSegList(this);
return this._pathSegList;
},
enumerable: true,
});
Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", {
get: function () {
return this.pathSegList;
},
enumerable: true,
});
Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", {
get: function () {
return this.pathSegList;
},
enumerable: true,
});
Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", {
get: function () {
return this.pathSegList;
},
enumerable: true,
});
window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () {
this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords());
};
window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) {
if (!this._pathElement)
return;
let hasPathMutations = false;
mutationRecords.forEach(function (record) {
if (record.attributeName == "d")
hasPathMutations = true;
});
if (hasPathMutations)
this._list = this._parsePath(this._pathElement.getAttribute("d"));
};
window.SVGPathSegList.prototype._writeListToPath = function () {
this._pathElementMutationObserver.disconnect();
this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
};
window.SVGPathSegList.prototype.segmentChanged = function () {
this._writeListToPath();
};
window.SVGPathSegList.prototype.clear = function () {
this._checkPathSynchronizedToList();
this._list.forEach(function (pathSeg) {
pathSeg._owningPathSegList = null;
});
this._list = [];
this._writeListToPath();
};
window.SVGPathSegList.prototype.initialize = function (newItem) {
this._checkPathSynchronizedToList();
this._list = [newItem];
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
};
window.SVGPathSegList.prototype._checkValidIndex = function (index) {
if (isNaN(index) || index < 0 || index >= this.numberOfItems)
throw "INDEX_SIZE_ERR";
};
window.SVGPathSegList.prototype.getItem = function (index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
return this._list[index];
};
window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) {
this._checkPathSynchronizedToList();
if (index > this.numberOfItems)
index = this.numberOfItems;
if (newItem._owningPathSegList) {
newItem = newItem.clone();
}
this._list.splice(index, 0, newItem);
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
};
window.SVGPathSegList.prototype.replaceItem = function (newItem, index) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
newItem = newItem.clone();
}
this._checkValidIndex(index);
this._list[index] = newItem;
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
};
window.SVGPathSegList.prototype.removeItem = function (index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
const item = this._list[index];
this._list.splice(index, 1);
this._writeListToPath();
return item;
};
window.SVGPathSegList.prototype.appendItem = function (newItem) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
newItem = newItem.clone();
}
this._list.push(newItem);
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
};
window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) {
let string = "";
let first = true;
pathSegArray.forEach(function (pathSeg) {
if (first) {
first = false;
string += pathSeg._asPathString();
}
else {
string += ` ${pathSeg._asPathString()}`;
}
});
return string;
};
window.SVGPathSegList.prototype._parsePath = function (string) {
if (!string?.length)
return [];
const owningPathSegList = this;
const Builder = function () {
this.pathSegList = [];
};
Builder.prototype.appendSegment = function (pathSeg) {
this.pathSegList.push(pathSeg);
};
const Source = function (string) {
this._string = string;
this._currentIndex = 0;
this._endIndex = this._string.length;
this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN;
this._skipOptionalSpaces();
};
Source.prototype._isCurrentSpace = function () {
const character = this._string[this._currentIndex];
return (character <= " " &&
(character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f"));
};
Source.prototype._skipOptionalSpaces = function () {
while (this._currentIndex < this._endIndex && this._isCurrentSpace())
this._currentIndex++;
return this._currentIndex < this._endIndex;
};
Source.prototype._skipOptionalSpacesOrDelimiter = function () {
if (this._currentIndex < this._endIndex &&
!this._isCurrentSpace() &&
this._string.charAt(this._currentIndex) != ",")
return false;
if (this._skipOptionalSpaces()) {
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") {
this._currentIndex++;
this._skipOptionalSpaces();
}
}
return this._currentIndex < this._endIndex;
};
Source.prototype.hasMoreData = function () {
return this._currentIndex < this._endIndex;
};
Source.prototype.peekSegmentType = function () {
const lookahead = this._string[this._currentIndex];
return this._pathSegTypeFromChar(lookahead);
};
Source.prototype._pathSegTypeFromChar = function (lookahead) {
switch (lookahead) {
case "Z":
case "z":
return window.SVGPathSeg.PATHSEG_CLOSEPATH;
case "M":
return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
case "m":
return window.SVGPathSeg.PATHSEG_MOVETO_REL;
case "L":
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
case "l":
return window.SVGPathSeg.PATHSEG_LINETO_REL;
case "C":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
case "c":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
case "Q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
case "q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
case "A":
return window.SVGPathSeg.PATHSEG_ARC_ABS;
case "a":
return window.SVGPathSeg.PATHSEG_ARC_REL;
case "H":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
case "h":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
case "V":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
case "v":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
case "S":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
case "s":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
case "T":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
case "t":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
default:
return window.SVGPathSeg.PATHSEG_UNKNOWN;
}
};
Source.prototype._nextCommandHelper = function (lookahead, previousCommand) {
if ((lookahead == "+" || lookahead == "-" || lookahead == "." || (lookahead >= "0" && lookahead <= "9")) &&
previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS)
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL)
return window.SVGPathSeg.PATHSEG_LINETO_REL;
return previousCommand;
}
return window.SVGPathSeg.PATHSEG_UNKNOWN;
};
Source.prototype.initialCommandIsMoveTo = function () {
if (!this.hasMoreData())
return true;
const command = this.peekSegmentType();
return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL;
};
Source.prototype._parseNumber = function () {
let exponent = 0;
let integer = 0;
let frac = 1;
let decimal = 0;
let sign = 1;
let expsign = 1;
const startIndex = this._currentIndex;
this._skipOptionalSpaces();
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+")
this._currentIndex++;
else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
sign = -1;
}
if (this._currentIndex == this._endIndex ||
((this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") &&
this._string.charAt(this._currentIndex) != "."))
return undefined;
const startIntPartIndex = this._currentIndex;
while (this._currentIndex < this._endIndex &&
this._string.charAt(this._currentIndex) >= "0" &&
this._string.charAt(this._currentIndex) <= "9")
this._currentIndex++;
if (this._currentIndex != startIntPartIndex) {
let scanIntPartIndex = this._currentIndex - 1;
let multiplier = 1;
while (scanIntPartIndex >= startIntPartIndex) {
integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
multiplier *= 10;
}
}
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
this._currentIndex++;
if (this._currentIndex >= this._endIndex ||
this._string.charAt(this._currentIndex) < "0" ||
this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex &&
this._string.charAt(this._currentIndex) >= "0" &&
this._string.charAt(this._currentIndex) <= "9") {
frac *= 10;
decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
this._currentIndex += 1;
}
}
if (this._currentIndex != startIndex &&
this._currentIndex + 1 < this._endIndex &&
(this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") &&
this._string.charAt(this._currentIndex + 1) != "x" &&
this._string.charAt(this._currentIndex + 1) != "m") {
this._currentIndex++;
if (this._string.charAt(this._currentIndex) == "+") {
this._currentIndex++;
}
else if (this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
expsign = -1;
}
if (this._currentIndex >= this._endIndex ||
this._string.charAt(this._currentIndex) < "0" ||
this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex &&
this._string.charAt(this._currentIndex) >= "0" &&
this._string.charAt(this._currentIndex) <= "9") {
exponent *= 10;
exponent += this._string.charAt(this._currentIndex) - "0";
this._currentIndex++;
}
}
let number = integer + decimal;
number *= sign;
if (exponent)
number *= Math.pow(10, expsign * exponent);
if (startIndex == this._currentIndex)
return undefined;
this._skipOptionalSpacesOrDelimiter();
return number;
};
Source.prototype._parseArcFlag = function () {
if (this._currentIndex >= this._endIndex)
return undefined;
let flag = false;
const flagChar = this._string.charAt(this._currentIndex++);
if (flagChar == "0")
flag = false;
else if (flagChar == "1")
flag = true;
else
return undefined;
this._skipOptionalSpacesOrDelimiter();
return flag;
};
Source.prototype.parseSegment = function () {
const lookahead = this._string[this._currentIndex];
let command = this._pathSegTypeFromChar(lookahead);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {
if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
command = this._nextCommandHelper(lookahead, this._previousCommand);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
}
else {
this._currentIndex++;
}
this._previousCommand = command;
let points;
switch (command) {
case window.SVGPathSeg.PATHSEG_MOVETO_REL:
return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_MOVETO_ABS:
return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_REL:
return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_ABS:
return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_CLOSEPATH:
this._skipOptionalSpaces();
return new window.SVGPathSegClosePath(owningPathSegList);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
x2: this._parseNumber(),
y2: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
x2: this._parseNumber(),
y2: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
points = {
x2: this._parseNumber(),
y2: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
points = {
x2: this._parseNumber(),
y2: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_ARC_REL:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
arcAngle: this._parseNumber(),
arcLarge: this._parseArcFlag(),
arcSweep: this._parseArcFlag(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
case window.SVGPathSeg.PATHSEG_ARC_ABS:
points = {
x1: this._parseNumber(),
y1: this._parseNumber(),
arcAngle: this._parseNumber(),
arcLarge: this._parseArcFlag(),
arcSweep: this._parseArcFlag(),
x: this._parseNumber(),
y: this._parseNumber(),
};
return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
default:
throw "Unknown path seg type.";
}
};
const builder = new Builder();
const source = new Source(string);
if (!source.initialCommandIsMoveTo())
return [];
while (source.hasMoreData()) {
const pathSeg = source.parseSegment();
if (!pathSeg)
return [];
builder.appendSegment(pathSeg);
}
return builder.pathSegList;
};
}
}
catch (e) {
console.warn("An error occurred in tsParticles pathseg polyfill. If the Polygon Mask is not working, please open an issue here: https://github.com/tsparticles/tsparticles", e);
}
})();
const inSegmentRange = {
min: 0,
max: 1,
};
function drawPolygonMask(pluginManager, context, rawData, stroke, hdr = false, peakNits, mode) {
const color = rangeColorToRgb(pluginManager, stroke.color);
if (!color) {
return;
}
const firstIndex = 0, firstItem = rawData[firstIndex];
if (!firstItem) {
return;
}
context.beginPath();
context.moveTo(firstItem.x, firstItem.y);
for (const item of rawData) {
context.lineTo(item.x, item.y);
}
context.closePath();
context.strokeStyle = getStyleFromRgb(color, hdr, undefined, peakNits, mode);
context.lineWidth = stroke.width;
context.stroke();
}
function drawPolygonMaskPath(pluginManager, context, path, stroke, position, hdr = false, peakNits, mode) {
const defaultTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};
context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
const color = rangeColorToRgb(pluginManager, stroke.color);
if (!color) {
return;
}
context.strokeStyle = getStyleFromRgb(color, hdr, stroke.opacity, peakNits, mode);
context.lineWidth = stroke.width;
context.stroke(path);
context.resetTransform();
}
function parsePaths(paths, scale, offset) {
const res = [], defaultCount = 0;
for (const path of paths) {
const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
x: 0,
y: 0,
};
for (let i = 0; i < len; i++) {
const segment = segments?.getItem(i), svgPathSeg = globalThis.SVGPathSeg;
switch (segment?.pathSegType) {
case svgPathSeg.PATHSEG_MOVETO_ABS:
case svgPathSeg.PATHSEG_LINETO_ABS:
case svgPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
case svgPathSeg.PATHSEG_ARC_ABS:
case svgPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: {
const absSeg = segment;
p.x = absSeg.x;
p.y = absSeg.y;
break;
}
case svgPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
p.x = segment.x;
break;
case svgPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
p.y = segment.y;
break;
case svgPathSeg.PATHSEG_LINETO_REL:
case svgPathSeg.PATHSEG_MOVETO_REL:
case svgPathSeg.PATHSEG_CURVETO_CUBIC_REL:
case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
case svgPathSeg.PATHSEG_ARC_REL:
case svgPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: {
const relSeg = segment;
p.x += relSeg.x;
p.y += relSeg.y;
break;
}
case svgPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
p.x += segment.x;
break;
case svgPathSeg.PATHSEG_LINETO_VERTICAL_REL:
p.y += segment.y;
break;
case svgPathSeg.PATHSEG_UNKNOWN:
case svgPathSeg.PATHSEG_CLOSEPATH:
continue;
default:
continue;
}
res.push({
x: p.x * scale + offset.x,
y: p.y * scale + offset.y,
});
}
}
return res;
}
function calcClosestPointOnSegment(s1, s2, pos) {
const { dx: dx1, dy: dy1 } = getDistances(pos, s1), { dx: dx2, dy: dy2 } = getDistances(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** squareExp + dy2 ** squareExp), res = {
x: s1.x + dx2 * t,
y: s1.y + dy2 * t,
isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
};
if (t < inSegmentRange.min) {
res.x = s1.x;
res.y = s1.y;
}
else if (t > inSegmentRange.max) {
res.x = s2.x;
res.y = s2.y;
}
return res;
}
function segmentBounce(start, stop, velocity) {
const { dx, dy } = getDistances(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = double * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
wallNormal.multTo(d);
velocity.subFrom(wallNormal);
}
const noPolygonDataLoaded = `No polygon data loaded.`, noPolygonFound = `No polygon found, you need to specify SVG url in config.`;
class PolygonMaskInstance {
dimension;
offset;
paths;
raw;
redrawTimeout;
#container;
#moveRadius;
#pluginManager;
#scale;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
this.dimension = {
height: 0,
width: 0,
};
this.#moveRadius = 0;
this.#scale = 1;
}
clickPositionValid(position) {
const options = this.#container.actualOptions.polygon;
return (!!options?.enable &&
options.type !== PolygonMaskType.none &&
options.type !== PolygonMaskType.inline &&
this.#checkInsidePolygon(position));
}
draw(context) {
if (!this.paths?.length) {
return;
}
const options = this.#container.actualOptions.polygon;
if (!options?.enable) {
return;
}
const polygonDraw = options.draw;
if (!polygonDraw.enable) {
return;
}
const rawData = this.raw;
for (const path of this.paths) {
const path2d = path.path2d;
if (path2d && this.offset) {
drawPolygonMaskPath(this.#pluginManager, context, path2d, polygonDraw.stroke, this.offset, this.#container.hdr, this.#container.peakNits, this.#container.hdrMode);
}
else if (rawData) {
drawPolygonMask(this.#pluginManager, context, rawData, polygonDraw.stroke, this.#container.hdr, this.#container.peakNits, this.#container.hdrMode);
}
}
}
async init() {
const container = this.#container, polygonMaskOptions = container.actualOptions.polygon, pxRatio = container.retina.pixelRatio;
if (!polygonMaskOptions) {
return;
}
this.#moveRadius = polygonMaskOptions.move.radius * pxRatio;
this.#scale = polygonMaskOptions.scale * pxRatio;
if (polygonMaskOptions.enable) {
await this.#initRawData();
}
}
particleBounce(particle, delta, direction) {
return this.#polygonBounce(particle, delta, direction);
}
particlePosition(position) {
const options = this.#container.actualOptions.polygon, defaultLength = 0;
if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
return;
}
return deepExtend({}, position ?? this.#randomPoint());
}
particlesInitialization() {
const options = this.#container.actualOptions.polygon;
if (options?.enable &&
options.type === PolygonMaskType.inline &&
(options.inline.arrangement === PolygonMaskInlineArrangement.onePerPoint ||
options.inline.arrangement === PolygonMaskInlineArrangement.perPoint)) {
this.#drawPoints();
return true;
}
return false;
}
resize() {
const container = this.#container, options = container.actualOptions.polygon;
if (!(options?.enable && options.type !== PolygonMaskType.none)) {
return;
}
if (this.redrawTimeout) {
clearTimeout(this.redrawTimeout);
}
const timeout = 250;
this.redrawTimeout = setTimeout(() => {
void (async () => {
await this.#initRawData(true);
await container.particles.redraw();
})();
}, timeout);
}
stop() {
delete this.raw;
delete this.paths;
}
#checkInsidePolygon(position) {
const container = this.#container, options = container.actualOptions.polygon;
if (!options?.enable || options.type === PolygonMaskType.none || options.type === PolygonMaskType.inline) {
return true;
}
if (!this.raw) {
throw new Error(noPolygonFound);
}
const canvasSize = container.canvas.size, x = position?.x ?? getRandom() * canvasSize.width, y = position?.y ?? getRandom() * canvasSize.height, indexOffset = 1;
let inside = false;
for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
const pi = this.raw[i], pj = this.raw[j];
if (!pi || !pj) {
continue;
}
const intersect = pi.y > y !== pj.y > y && x < ((pj.x - pi.x) * (y - pi.y)) / (pj.y - pi.y) + pi.x;
if (intersect) {
inside = !inside;
}
}
if (options.type === PolygonMaskType.inside) {
return inside;
}
else {
return !inside;
}
}
#createPath2D() {
const container = this.#container, options = container.actualOptions.polygon;
if (!options || !this.paths?.length) {
return;
}
for (const path of this.paths) {
const pathData = path.element.getAttribute("d");
if (pathData) {
const path2d = new Path2D(pathData), matrix = safeDocument().createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix(), finalPath = new Path2D(), transform = matrix.scale(this.#scale);
finalPath.addPath(path2d, transform);
path.path2d = finalPath;
}
else {
delete path.path2d;
}
if (path.path2d ?? !this.raw) {
continue;
}
path.path2d = new Path2D();
const firstIndex = 0, firstPoint = this.raw[firstIndex];
if (!firstPoint) {
continue;
}
path.path2d.moveTo(firstPoint.x, firstPoint.y);
this.raw.forEach((pos, i) => {
if (i > firstIndex) {
path.path2d?.lineTo(pos.x, pos.y);
}
});
path.path2d.closePath();
}
}
async #downloadSvgPath(svgUrl, force) {
const options = this.#container.actualOptions.polygon;
if (!options) {
return;
}
const url = svgUrl ?? options.url, forceDownload = force ?? false;
if (!url || (this.paths !== undefined && !forceDownload)) {
return this.raw;
}
const req = await fetch(url);
if (!req.ok) {
throw new Error(`Error occurred during polygon mask download`);
}
return this.#parseSvgPath(await req.text(), force);
}
#drawPoints() {
if (!this.raw) {
return;
}
for (const item of this.raw) {
this.#container.particles.addParticle({
x: item.x,
y: item.y,
});
}
}
#getEquidistantPointByIndex(index) {
const container = this.#container, options = container.actualOptions, polygonMaskOptions = options.polygon;
if (!polygonMaskOptions) {
return;
}
if (!this.raw?.length || !this.paths?.length) {
throw new Error(noPolygonDataLoaded);
}
let offset = 0, point;
const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
for (const path of this.paths) {
const pathDistance = distance * index - offset;
if (pathDistance <= path.length) {
point = path.element.getPointAtLength(pathDistance);
break;
}
else {
offset += path.length;
}
}
const scale = this.#scale;
return {
x: (point?.x ?? originPoint.x) * scale + (this.offset?.x ?? originPoint.x),
y: (point?.y ?? originPoint.y) * scale + (this.offset?.y ?? originPoint.y),
};
}
#getPointByIndex(index) {
if (!this.raw?.length) {
throw new Error(noPolygonDataLoaded);
}
const coords = this.raw[index % this.raw.length];
if (!coords) {
return;
}
return {
x: coords.x,
y: coords.y,
};
}
#getRandomPoint() {
if (!this.raw?.length) {
throw new Error(noPolygonDataLoaded);
}
const coords = itemFromArray(this.raw);
if (!coords) {
return;
}
return {
x: coords.x,
y: coords.y,
};
}
#getRandomPointByLength() {
const container = this.#container, options = container.actualOptions.polygon;
if (!options) {
return;
}
if (!this.raw?.length || !this.paths?.length) {
throw new Error(noPolygonDataLoaded);
}
const path = itemFromArray(this.paths);
if (!path) {
return;
}
const offset = 1, distance = Math.floor(getRandom() * path.length) + offset, point = path.element.getPointAtLength(distance), scale = this.#scale;
return {
x: point.x * scale + (this.offset?.x ?? originPoint.x),
y: point.y * scale + (this.offset?.y ?? originPoint.y),
};
}
async #initRawData(force) {
const options = this.#container.actualOptions.polygon;
if (!options) {
return;
}
if (options.url) {
this.raw = await this.#downloadSvgPath(options.url, force);
}
else if (options.data) {
const data = options.data;
let svg;
if (isString(data)) {
svg = data;
}
else {
const getPath = (p) => `<path d="${p}" />`, path = isArray(data.path) ? data.path.map(getPath).join("") : getPath(data.path), namespaces = 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"';
svg = `<svg ${namespaces} width="${data.size.width.toString()}" height="${data.size.height.toString()}">${path}</svg>`;
}
this.raw = this.#parseSvgPath(svg, force);
}
this.#createPath2D();
this.#container.dispatchEvent("polygonMaskLoaded");
}
#parseSvgPath(xml, force) {
const forceDownload = force ?? false;
if (this.paths !== undefined && !forceDownload) {
return this.raw;
}
const container = this.#container, options = container.actualOptions.polygon;
if (!options) {
return;
}
const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
if (!svg) {
return;
}
let svgPaths = svg.getElementsByTagName("path");
if (!svgPaths.length) {
svgPaths = doc.getElementsByTagName("path");
}
this.paths = [];
for (let i = 0; i < svgPaths.length; i++) {
const path = svgPaths.item(i);
if (path) {
this.paths.push({
element: path,
length: path.getTotalLength(),
});
}
}
const scale = this.#scale;
this.dimension.width = parseFloat(svg.getAttribute("width") ?? "0") * scale;
this.dimension.height = parseFloat(svg.getAttribute("height") ?? "0") * scale;
const position = options.position ?? {
x: 50,
y: 50,
}, canvasSize = container.canvas.size;
this.offset = {
x: (canvasSize.width * position.x) / percentDenominator - this.dimension.width * half$2,
y: (canvasSize.height * position.y) / percentDenominator - this.dimension.height * half$2,
};
return parsePaths(this.paths, scale, this.offset);
}
#polygonBounce(particle, _delta, direction) {
const options = this.#container.actualOptions.polygon;
if (!this.raw || !options?.enable || direction !== exports.OutModeDirection.top) {
return false;
}
if (options.type === PolygonMaskType.inside || options.type === PolygonMaskType.outside) {
let closest, dx, dy;
const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
const pi = this.raw[i], pj = this.raw[j];
if (!pi || !pj) {
continue;
}
closest = calcClosestPointOnSegment(pi, pj, pos);
const dist = getDistances(pos, closest);
[dx, dy] = [dist.dx, dist.dy];
if (dist.distance < radius) {
segmentBounce(pi, pj, particle.velocity);
return true;
}
}
if (closest && dx !== undefined && dy !== undefined && !this.#checkInsidePolygon(pos)) {
const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
if (pos.x >= closest.x) {
factor.x = -1;
}
if (pos.y >= closest.y) {
factor.y = -1;
}
particle.position.x = closest.x + diameter * factor.x;
particle.position.y = closest.y + diameter * factor.y;
particle.velocity.mult(inverse);
return true;
}
}
else if (options.type === PolygonMaskType.inline) {
const dist = getDistance(particle.initialPosition, particle.getPosition()), { velocity } = particle;
if (dist > this.#moveRadius) {
velocity.x = velocity.y * half$2 - velocity.x;
velocity.y = velocity.x * half$2 - velocity.y;
return true;
}
}
return false;
}
#randomPoint() {
const container = this.#container, options = container.actualOptions.polygon;
if (!options) {
return;
}
let position;
if (options.type === PolygonMaskType.inline) {
switch (options.inline.arrangement) {
case PolygonMaskInlineArrangement.randomPoint:
position = this.#getRandomPoint();
break;
case PolygonMaskInlineArrangement.randomLength:
position = this.#getRandomPointByLength();
break;
case PolygonMaskInlineArrangement.equidistant:
position = this.#getEquidistantPointByIndex(container.particles.count);
break;
case PolygonMaskInlineArrangement.onePerPoint:
case PolygonMaskInlineArrangement.perPoint:
default:
position = this.#getPointByIndex(container.particles.count);
}
}
else {
const canvasSize = container.canvas.size;
position = {
x: getRandom() * canvasSize.width,
y: getRandom() * canvasSize.height,
};
}
if (this.#checkInsidePolygon(position)) {
return position;
}
else {
return this.#randomPoint();
}
}
}
var PolygonMaskInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
PolygonMaskInstance: PolygonMaskInstance
});
class ResponsivePluginInstance {
#container;
constructor(container) {
this.#container = container;
}
updateActualOptions() {
const container = this.#container;
container.actualOptions.responsive = [];
const newMaxWidth = container.actualOptions.setResponsive?.(container.canvas.size.width, container.retina.pixelRatio, container.options);
if (container.responsiveMaxWidth === newMaxWidth) {
return false;
}
container.responsiveMaxWidth = newMaxWidth;
return true;
}
}
var ResponsivePluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ResponsivePluginInstance: ResponsivePluginInstance
});
var SoundsEventType;
(function (SoundsEventType) {
SoundsEventType["mute"] = "soundsMuted";
SoundsEventType["unmute"] = "soundsUnmuted";
})(SoundsEventType || (SoundsEventType = {}));
var ImageDisplay;
(function (ImageDisplay) {
ImageDisplay["Block"] = "block";
ImageDisplay["None"] = "none";
})(ImageDisplay || (ImageDisplay = {}));
const zIndexOffset = 1, rightOffset = 1, minVolume = 0;
function initImage(data) {
const img = safeDocument().createElement("img"), { clickCb, container, display, iconOptions, margin, options, pos, rightOffsets } = data, { width, path, style, svg } = iconOptions, defaultAccumulator = 0;
setIconStyle(img, pos.top + margin, pos.right -
(margin * (rightOffsets.length + rightOffset) + width + rightOffsets.reduce((a, b) => a + b, defaultAccumulator)), display, options.fullScreen.zIndex + zIndexOffset, width, margin, style);
img.src = path ?? (svg ? `data:image/svg+xml;base64,${btoa(svg)}` : "");
const parent = container.canvas.domElement?.parentNode ?? safeDocument().body;
parent.append(img);
img.addEventListener("click", () => {
void clickCb();
});
return img;
}
function removeImage(image) {
if (!image) {
return;
}
image.remove();
}
function setIconStyle(icon, top, left, display, zIndex, width, margin, style) {
icon.style.userSelect = "none";
icon.style.position = "absolute";
icon.style.top = `${(top + margin).toString()}px`;
icon.style.left = `${(left - margin - width).toString()}px`;
icon.style.display = display;
icon.style.zIndex = (zIndex + zIndexOffset).toString();
icon.style.cssText += style;
}
class SoundsPluginInstance {
#audioMap;
#audioSources;
#container;
#engine;
#gain;
#muteImg;
#unmuteImg;
#volume;
#volumeDownImg;
#volumeUpImg;
constructor(container, engine) {
this.#container = container;
this.#engine = engine;
this.#volume = 0;
this.#audioSources = [];
this.#audioMap = new Map();
}
async init() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions?.enable) {
return;
}
if (soundsOptions.autoPlay && isWindowMuted()) {
const firstClickHandler = () => {
removeEventListener(mouseDownEvent, firstClickHandler);
removeEventListener(touchStartEvent, firstClickHandler);
unmuteWindow();
void this.unmute();
}, listenerOptions = {
capture: true,
once: true,
};
addEventListener(mouseDownEvent, firstClickHandler, listenerOptions);
addEventListener(touchStartEvent, firstClickHandler, listenerOptions);
}
this.#volume = soundsOptions.volume.value;
const events = soundsOptions.events;
this.#audioMap = new Map();
for (const event of events) {
if (!event.audio) {
continue;
}
const promises = executeOnSingleOrMultiple(event.audio, async (audio) => {
const response = await fetch(audio.source);
if (!response.ok) {
return;
}
const arrayBuffer = await response.arrayBuffer(), audioContext = this.#getAudioContext(), audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
this.#audioMap.set(audio.source, audioBuffer);
});
if (promises instanceof Promise) {
await promises;
}
else {
await Promise.allSettled(promises);
}
}
}
async mute() {
if (!this.#container.muted) {
await this.toggleMute();
}
}
async start() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions?.enable || !container.canvas.domElement) {
return;
}
container.muted = true;
const canvas = container.canvas.domElement, pos = {
top: canvas.offsetTop,
right: canvas.offsetLeft + canvas.offsetWidth,
}, { mute, unmute, volumeDown, volumeUp } = soundsOptions.icons, margin = 10, toggleMute = async () => {
await this.toggleMute();
}, enableIcons = soundsOptions.icons.enable, display = enableIcons ? ImageDisplay.Block : ImageDisplay.None;
this.#muteImg = initImage({
container,
options,
pos,
display,
iconOptions: mute,
margin,
rightOffsets: [volumeDown.width, volumeUp.width],
clickCb: toggleMute,
});
this.#unmuteImg = initImage({
container,
options,
pos,
display: ImageDisplay.None,
iconOptions: unmute,
margin,
rightOffsets: [volumeDown.width, volumeUp.width],
clickCb: toggleMute,
});
this.#volumeDownImg = initImage({
container,
options,
pos,
display,
iconOptions: volumeDown,
margin,
rightOffsets: [volumeUp.width],
clickCb: async () => {
await this.volumeDown();
},
});
this.#volumeUpImg = initImage({
container,
options,
pos,
display,
iconOptions: volumeUp,
margin,
rightOffsets: [],
clickCb: async () => {
await this.volumeUp();
},
});
if (!isWindowMuted() && soundsOptions.autoPlay) {
await this.unmute();
}
}
stop() {
this.#container.muted = true;
void (async () => {
await this.#mute();
removeImage(this.#muteImg);
removeImage(this.#unmuteImg);
removeImage(this.#volumeDownImg);
removeImage(this.#volumeUpImg);
})();
}
async toggleMute() {
const container = this.#container;
container.muted = !container.muted;
this.#updateMuteIcons();
await this.#updateMuteStatus();
}
async unmute() {
if (this.#container.muted) {
await this.toggleMute();
}
}
async volumeDown() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
if (container.muted) {
this.#volume = 0;
}
this.#volume -= soundsOptions.volume.step;
await this.#updateVolume();
}
async volumeUp() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
this.#volume += soundsOptions.volume.step;
await this.#updateVolume();
}
#addBuffer(audioCtx) {
const buffer = audioCtx.createBufferSource();
this.#audioSources.push(buffer);
return buffer;
}
#addOscillator(audioCtx) {
const oscillator = audioCtx.createOscillator();
this.#audioSources.push(oscillator);
return oscillator;
}
#getAudioContext() {
const container = this.#container;
container.audioContext ??= new AudioContext();
return container.audioContext;
}
#initEvents() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable || !container.canvas.domElement) {
return;
}
for (const event of soundsOptions.events) {
const cb = (args) => {
if (!args) {
return;
}
void (async () => {
const filterNotValid = event.filter && !event.filter(args);
if (this.#container !== args.container) {
return;
}
if (!!this.#container.muted || this.#container.destroyed) {
executeOnSingleOrMultiple(event.event, item => {
this.#engine.removeEventListener(item, cb);
});
return;
}
if (filterNotValid) {
return;
}
const defaultNoteIndex = 0;
if (event.audio) {
const audio = itemFromSingleOrMultiple(event.audio);
if (!audio) {
return;
}
this.#playBuffer(audio);
}
else if (event.melodies) {
const melody = itemFromArray(event.melodies);
if (!melody) {
return;
}
if (melody.melodies.length) {
await Promise.allSettled(melody.melodies.map(m => this.#playNote(m.notes, defaultNoteIndex, melody.loop)));
}
else {
await this.#playNote(melody.notes, defaultNoteIndex, melody.loop);
}
}
else if (event.notes) {
const note = itemFromArray(event.notes);
if (!note) {
return;
}
await this.#playNote([note], defaultNoteIndex, false);
}
})();
};
executeOnSingleOrMultiple(event.event, item => {
this.#engine.addEventListener(item, cb);
});
}
}
async #mute() {
const container = this.#container, audioContext = this.#getAudioContext();
for (const source of this.#audioSources) {
this.#removeAudioSource(source);
}
if (this.#gain) {
this.#gain.disconnect();
}
await audioContext.close();
container.audioContext = undefined;
this.#container.dispatchEvent(SoundsEventType.mute);
}
#playBuffer(audio) {
const audioBuffer = this.#audioMap.get(audio.source);
if (!audioBuffer) {
return;
}
const audioCtx = this.#container.audioContext;
if (!audioCtx) {
return;
}
const source = this.#addBuffer(audioCtx);
source.loop = audio.loop;
source.buffer = audioBuffer;
source.connect(this.#gain ?? audioCtx.destination);
source.start();
}
async #playFrequency(frequency, duration) {
if (!this.#gain || this.#container.muted) {
return;
}
const audioContext = this.#getAudioContext(), oscillator = this.#addOscillator(audioContext);
oscillator.connect(this.#gain);
oscillator.type = "sine";
oscillator.frequency.value = frequency;
oscillator.start();
return new Promise(resolve => {
setTimeout(() => {
this.#removeAudioSource(oscillator);
resolve();
}, duration);
});
}
#playMuteSound() {
if (this.#container.muted) {
return;
}
const audioContext = this.#getAudioContext(), gain = audioContext.createGain();
gain.connect(audioContext.destination);
gain.gain.value = 0;
const oscillator = audioContext.createOscillator();
oscillator.connect(gain);
oscillator.type = "sine";
oscillator.frequency.value = 1;
oscillator.start();
setTimeout(() => {
oscillator.stop();
oscillator.disconnect();
gain.disconnect();
});
}
async #playNote(notes, noteIdx, loop) {
if (this.#container.muted) {
return;
}
const note = notes[noteIdx];
if (!note) {
return;
}
const value = note.value, promises = executeOnSingleOrMultiple(value, async (_, idx) => {
return this.#playNoteValue(notes, noteIdx, idx);
});
await (isArray(promises) ? Promise.allSettled(promises) : promises);
const indexOffset = 1;
let nextNoteIdx = noteIdx + indexOffset;
if (loop && nextNoteIdx >= notes.length) {
nextNoteIdx = nextNoteIdx % notes.length;
}
await this.#playNote(notes, nextNoteIdx, loop);
}
async #playNoteValue(notes, noteIdx, valueIdx) {
const note = notes[noteIdx];
if (!note) {
return;
}
const value = itemFromSingleOrMultiple(note.value, valueIdx, true);
if (!value) {
return;
}
try {
const freq = getNoteFrequency(value);
if (!isNumber(freq)) {
return;
}
await this.#playFrequency(freq, note.duration);
}
catch (e) {
getLogger().error(e);
}
}
#removeAudioSource(source) {
source.stop();
source.disconnect();
const deleteCount = 1;
this.#audioSources.splice(this.#audioSources.indexOf(source), deleteCount);
}
#unmute() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions) {
return;
}
const audioContext = this.#getAudioContext(), gain = audioContext.createGain();
gain.connect(audioContext.destination);
gain.gain.value = soundsOptions.volume.value / percentDenominator;
this.#gain = gain;
this.#initEvents();
this.#container.dispatchEvent(SoundsEventType.unmute);
}
#updateMuteIcons() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable || !soundsOptions.icons.enable) {
return;
}
const muteImg = this.#muteImg, unmuteImg = this.#unmuteImg;
if (muteImg) {
muteImg.style.display = container.muted ? "block" : "none";
}
if (unmuteImg) {
unmuteImg.style.display = container.muted ? "none" : "block";
}
}
async #updateMuteStatus() {
const container = this.#container, audioContext = this.#getAudioContext();
if (container.muted) {
await audioContext.suspend();
await this.#mute();
}
else {
await audioContext.resume();
this.#unmute();
this.#playMuteSound();
}
}
async #updateVolume() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
clamp(this.#volume, soundsOptions.volume.min, soundsOptions.volume.max);
let stateChanged = false;
if (this.#volume <= minVolume && !container.muted) {
this.#volume = 0;
container.muted = true;
stateChanged = true;
}
else if (this.#volume > minVolume && container.muted) {
container.muted = false;
stateChanged = true;
}
if (stateChanged) {
this.#updateMuteIcons();
await this.#updateMuteStatus();
}
if (this.#gain?.gain) {
this.#gain.gain.value = this.#volume / percentDenominator;
}
}
}
var SoundsPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
SoundsPluginInstance: SoundsPluginInstance
});
class ThemesPluginInstance {
#container;
constructor(container) {
this.#container = container;
}
init() {
const container = this.#container;
container.themeMatchMedia = safeMatchMedia("(prefers-color-scheme: dark)");
const loadTheme = async (name) => {
if (container.destroyed) {
return;
}
container.currentTheme = name;
await container.refresh();
};
container.themeHandlers = {
themeChange: (e) => {
this.#handleThemeChange(e);
},
oldThemeChange: (e) => {
this.#handleThemeChange(e);
},
};
container.manageMediaMatch = (add) => {
const handlers = container.themeHandlers;
if (!handlers) {
return;
}
if (!container.themeMatchMedia) {
return;
}
manageListener(container.themeMatchMedia, "change", handlers.themeChange, add);
};
container.loadTheme = loadTheme;
return Promise.resolve();
}
manageListeners(add) {
const container = this.#container;
container.manageMediaMatch?.(add);
}
async start() {
this.manageListeners(true);
return Promise.resolve();
}
stop() {
this.manageListeners(false);
}
updateActualOptions() {
const container = this.#container;
container.actualOptions.setTheme?.(container.currentTheme);
return false;
}
#handleThemeChange(e) {
const mediaEvent = e, container = this.#container, options = container.options, defaultThemes = options.defaultThemes, themeName = mediaEvent.matches ? defaultThemes.dark : defaultThemes.light, theme = options.themes?.find(theme => theme.name === themeName);
if (theme?.default.auto) {
void container.loadTheme?.(themeName);
}
}
}
var ThemesPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ThemesPluginInstance: ThemesPluginInstance
});
const minimumLength = 0;
class TrailPluginInstance {
layer = exports.DrawLayer.PluginContent;
#container;
#pluginManager;
#trailFill;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
}
canvasClear() {
const container = this.#container, trail = container.actualOptions.trail, trailFill = this.#trailFill;
if (!trail?.enable || !trailFill || trail.length <= minimumLength) {
return false;
}
let handled = false;
const canvas = container.canvas;
if (trailFill.color) {
canvas.render.paintBase(getStyleFromRgb(trailFill.color, container.hdr, trailFill.opacity, container.peakNits, container.hdrMode));
handled = true;
}
else if (trailFill.image) {
canvas.render.paintImage(trailFill.image, trailFill.opacity);
handled = true;
}
return handled;
}
async init() {
try {
await this.#initTrail();
}
catch (e) {
getLogger().error(e);
}
}
async #initTrail() {
const options = this.#container.actualOptions, trail = options.trail;
if (!trail?.enable) {
return;
}
const trailFill = trail.fill, opacity = inverseFactorNumerator / trail.length;
if (trailFill.color) {
const fillColor = rangeColorToRgb(this.#pluginManager, trailFill.color);
if (!fillColor) {
return;
}
this.#trailFill = {
color: {
...fillColor,
},
opacity,
};
}
else {
await new Promise((resolve, reject) => {
if (!trailFill.image) {
return;
}
const img = safeDocument().createElement("img");
img.addEventListener("load", () => {
this.#trailFill = {
image: img,
opacity,
};
resolve();
});
img.addEventListener("error", evt => {
getLogger().error(evt);
reject(new Error("Error loading image"));
});
img.src = trailFill.image;
});
}
}
}
var TrailPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
TrailPluginInstance: TrailPluginInstance
});
class ZoomEventListeners {
#container;
#gestureScale;
#handlers;
#touchDistance;
constructor(container) {
this.#container = container;
this.#gestureScale = defaultZoom;
this.#touchDistance = initialTouchDistance;
this.#handlers = {
gestureStart: (e) => {
this.#handleGestureStart(e);
},
gestureChange: (e) => {
this.#handleGestureChange(e);
},
gestureEnd: (e) => {
this.#handleGestureEnd(e);
},
wheel: (e) => {
this.#handleMouseWheel(e);
},
touchMove: (e) => {
this.#handleTouchZoom(e);
},
touchEnd: () => {
this.#handleTouchEnd();
},
};
}
addListeners() {
this.#manageListeners(true);
}
removeListeners() {
this.#manageListeners(false);
}
#handleGestureChange(event) {
const container = this.#container, canvas = container.canvas, canvasEl = canvas.domElement;
if (!canvasEl) {
return;
}
const zoomOptions = container.actualOptions.zoom;
if (!zoomOptions?.enable) {
return;
}
if (!this.#isEventInsideCanvas(event, canvasEl)) {
return;
}
event.preventDefault();
const gestureEvent = event, baseZoom = defaultZoom, gestureFactor = zoomGestureFactor, scale = gestureEvent.scale ?? baseZoom;
if (scale === this.#gestureScale) {
return;
}
const scaleDelta = scale / this.#gestureScale, adjustedScale = baseZoom + (scaleDelta - baseZoom) * gestureFactor, currentZoom = canvas.zoom, newZoom = Math.max(zoomOptions.min, Math.min(currentZoom * adjustedScale, zoomOptions.max)), rect = canvasEl.getBoundingClientRect(), clientX = gestureEvent.clientX ?? rect.left + rect.width / touchCenterDivisor, clientY = gestureEvent.clientY ?? rect.top + rect.height / touchCenterDivisor, pixelRatio = this.#container.retina.pixelRatio, centerX = (clientX - rect.left) * pixelRatio, centerY = (clientY - rect.top) * pixelRatio;
canvas.setZoom(newZoom, { x: centerX, y: centerY });
this.#gestureScale = scale;
}
#handleGestureEnd(event) {
const container = this.#container, canvas = container.canvas, canvasEl = canvas.domElement;
if (!canvasEl) {
return;
}
const zoomOptions = container.actualOptions.zoom;
if (!zoomOptions?.enable) {
return;
}
if (!this.#isEventInsideCanvas(event, canvasEl)) {
return;
}
event.preventDefault();
this.#gestureScale = defaultZoom;
}
#handleGestureStart(event) {
const container = this.#container, canvas = container.canvas, canvasEl = canvas.domElement;
if (!canvasEl) {
return;
}
const zoomOptions = container.actualOptions.zoom;
if (!zoomOptions?.enable) {
return;
}
if (!this.#isEventInsideCanvas(event, canvasEl)) {
return;
}
event.preventDefault();
this.#gestureScale = defaultZoom;
}
#handleMouseWheel(event) {
if (!event.ctrlKey && !event.metaKey) {
return;
}
const container = this.#container, zoomOptions = container.actualOptions.zoom, canvas = container.canvas, canvasEl = canvas.domElement;
if (!canvasEl) {
return;
}
if (!zoomOptions?.enable) {
return;
}
if (!this.#isEventInsideCanvas(event, canvasEl)) {
return;
}
event.preventDefault();
const zoomFactor = event.deltaY > initialTouchDistance ? zoomOutFactor : zoomInFactor, currentZoom = canvas.zoom, newZoom = Math.max(zoomOptions.min, Math.min(currentZoom * zoomFactor, zoomOptions.max)), rect = canvasEl.getBoundingClientRect(), pixelRatio = this.#container.retina.pixelRatio, mouseX = (event.clientX - rect.left) * pixelRatio, mouseY = (event.clientY - rect.top) * pixelRatio;
canvas.setZoom(newZoom, { x: mouseX, y: mouseY });
}
#handleTouchEnd() {
this.#touchDistance = initialTouchDistance;
}
#handleTouchZoom(event) {
if (event.touches.length !== touchPointsCount) {
return;
}
event.preventDefault();
const touch1 = event.touches[touchPointIndexFirst], touch2 = event.touches[touchPointIndexSecond];
if (!touch1 || !touch2) {
return;
}
const distance = Math.hypot(touch1.clientX - touch2.clientX, touch1.clientY - touch2.clientY);
if (this.#touchDistance === initialTouchDistance) {
this.#touchDistance = distance;
return;
}
const container = this.#container, zoomOptions = container.actualOptions.zoom, canvas = container.canvas, canvasEl = canvas.domElement;
if (!canvasEl) {
return;
}
if (!zoomOptions?.enable) {
return;
}
if (!this.#isEventInsideCanvas(event, canvasEl)) {
return;
}
const scale = distance / this.#touchDistance, baseZoom = defaultZoom, gestureFactor = zoomGestureFactor, adjustedScale = baseZoom + (scale - baseZoom) * gestureFactor, currentZoom = canvas.zoom, newZoom = Math.max(zoomOptions.min, Math.min(currentZoom * adjustedScale, zoomOptions.max)), rect = canvasEl.getBoundingClientRect(), pixelRatio = this.#container.retina.pixelRatio, centerX = ((touch1.clientX + touch2.clientX) / touchCenterDivisor - rect.left) * pixelRatio, centerY = ((touch1.clientY + touch2.clientY) / touchCenterDivisor - rect.top) * pixelRatio;
canvas.setZoom(newZoom, { x: centerX, y: centerY });
this.#touchDistance = distance;
}
#isEventInsideCanvas(event, canvasEl) {
if (event.target === canvasEl) {
return true;
}
const eventWithPath = event, path = eventWithPath.composedPath();
if (path.includes(canvasEl)) {
return true;
}
const { clientX, clientY } = eventWithPath;
if (typeof clientX === "number" && typeof clientY === "number") {
const rect = canvasEl.getBoundingClientRect();
return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
}
return false;
}
#manageListeners(add) {
const handlers = this.#handlers, canvas = this.#container.canvas.domElement, options = this.#container.actualOptions, doc = safeDocument();
if (!canvas) {
return;
}
if (add && !options.zoom?.enable) {
return;
}
const listenerOptions = { passive: false }, captureOptions = { capture: true, passive: false };
manageListener(doc, "gesturestart", handlers.gestureStart, add, captureOptions);
manageListener(doc, "gesturechange", handlers.gestureChange, add, captureOptions);
manageListener(doc, "gestureend", handlers.gestureEnd, add, captureOptions);
manageListener(doc, "wheel", handlers.wheel, add, captureOptions);
manageListener(canvas, "gesturestart", handlers.gestureStart, add, listenerOptions);
manageListener(canvas, "gesturechange", handlers.gestureChange, add, listenerOptions);
manageListener(canvas, "gestureend", handlers.gestureEnd, add, listenerOptions);
manageListener(canvas, "wheel", handlers.wheel, add, listenerOptions);
manageListener(canvas, "touchmove", handlers.touchMove, add, listenerOptions);
manageListener(canvas, "touchend", handlers.touchEnd, add, listenerOptions);
manageListener(canvas, "touchcancel", handlers.touchEnd, add, listenerOptions);
}
}
class ZoomPluginInstance {
layer = exports.DrawLayer.CanvasSetup;
#container;
#listeners;
constructor(container) {
this.#container = container;
this.#listeners = new ZoomEventListeners(container);
}
destroy() {
this.#listeners.removeListeners();
}
drawParticleTransform(data) {
const container = this.#container, canvas = container.canvas, zoom = canvas.zoom;
if (zoom === defaultZoom) {
return;
}
const center = canvas.getZoomCenter(), { context, position, transformData } = data, zoomedX = center.x + (position.x - center.x) * zoom, zoomedY = center.y + (position.y - center.y) * zoom;
data.drawScale = zoom;
data.drawRadius = data.radius * zoom;
data.drawPosition = { x: zoomedX, y: zoomedY };
context.setTransform(transformData.a * zoom, transformData.b * zoom, transformData.c * zoom, transformData.d * zoom, zoomedX, zoomedY);
}
drawSettingsCleanup(context) {
const zoom = this.#container.canvas.zoom;
if (zoom === defaultZoom) {
return;
}
context.restore();
}
drawSettingsSetup(context) {
const zoom = this.#container.canvas.zoom;
if (zoom === defaultZoom) {
return;
}
context.save();
const { x: centerX, y: centerY } = this.#container.canvas.getZoomCenter();
context.translate(centerX, centerY);
context.scale(zoom, zoom);
context.translate(-centerX, -centerY);
}
start() {
this.#listeners.addListeners();
return Promise.resolve();
}
stop() {
this.#listeners.removeListeners();
}
}
var ZoomPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ZoomPluginInstance: ZoomPluginInstance
});
const defaultLifeDelay$1 = 0, minLifeCount$1 = 0, defaultSpawnDelay$1 = 0, defaultEmitDelay = 0, defaultLifeCount$1 = -1, defaultColorAnimationFactor = 1, colorFactor = 3.6, defaultStrokeWidth = 1;
function setParticlesOptionsFillColor(particlesOptions, color, opacity, enable) {
const paint = (particlesOptions.paint ??= new Paint());
paint.fill = {
color: AnimatableColor.create(undefined, { value: color }),
enable,
opacity,
};
}
function setParticlesOptionsStrokeColor(particlesOptions, color, opacity, width) {
const paint = (particlesOptions.paint ??= new Paint());
paint.stroke = {
color: AnimatableColor.create(undefined, { value: color }),
opacity,
width,
};
}
class EmitterInstance {
fill;
name;
options;
position;
size;
spawnFillColor;
spawnFillEnabled;
spawnFillOpacity;
spawnStrokeColor;
spawnStrokeOpacity;
spawnStrokeWidth;
#container;
#currentDuration;
#currentEmitDelay;
#currentSpawnDelay;
#duration;
#emitDelay;
#firstSpawn;
#immortal;
#initialPosition;
#lifeCount;
#mutationObserver;
#particlesOptions;
#paused;
#pluginManager;
#removeCallback;
#resizeObserver;
#shape;
#size;
#spawnDelay;
#startParticlesAdded;
constructor(pluginManager, container, removeCallback, options, position) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#removeCallback = removeCallback;
this.#currentDuration = 0;
this.#currentEmitDelay = 0;
this.#currentSpawnDelay = 0;
this.#initialPosition = position;
if (options instanceof Emitter) {
this.options = options;
}
else {
this.options = new Emitter();
this.options.load(options);
}
this.#spawnDelay = container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay$1) * millisecondsToSeconds$1) /
container.retina.reduceFactor
: Infinity;
this.position = this.#initialPosition ?? this.#calcPosition();
this.name = this.options.name;
this.fill = this.options.fill;
this.#firstSpawn = !this.options.life.wait;
this.#startParticlesAdded = false;
const particlesOptions = deepExtend({}, this.options.particles);
particlesOptions.move ??= {};
particlesOptions.move.direction ??= this.options.direction;
if (this.options.spawn.fill?.color) {
this.spawnFillColor = rangeColorToHsl(this.#pluginManager, this.options.spawn.fill.color);
}
if (this.options.spawn.stroke?.color) {
this.spawnStrokeColor = rangeColorToHsl(this.#pluginManager, this.options.spawn.stroke.color);
}
this.#paused = !this.options.autoPlay;
this.#particlesOptions = particlesOptions;
this.#size = this.#calcSize();
this.size =
this.#size.mode === exports.PixelMode.percent
? {
width: (this.#size.width / percentDenominator) * this.#container.canvas.size.width,
height: (this.#size.height / percentDenominator) * this.#container.canvas.size.height,
}
: { width: this.#size.width, height: this.#size.height };
this.#lifeCount = this.options.life.count ?? defaultLifeCount$1;
this.#immortal = this.#lifeCount <= minLifeCount$1;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
this.#mutationObserver = new MutationObserver(() => {
this.resize();
});
this.#resizeObserver = new ResizeObserver(() => {
this.resize();
});
this.#mutationObserver.observe(element, {
attributes: true,
attributeFilter: ["style", "width", "height"],
});
this.#resizeObserver.observe(element);
}
}
const shapeOptions = this.options.shape, shapeGenerator = this.#pluginManager.emitterShapeManager?.getShapeGenerator(shapeOptions.type);
if (shapeGenerator) {
this.#shape = shapeGenerator.generate(this.#container, this.position, this.size, this.fill, shapeOptions.options);
}
this.#container.dispatchEvent("emitterCreated", {
emitter: this,
});
this.play();
}
externalPause() {
this.#paused = true;
this.pause();
}
externalPlay() {
this.#paused = false;
this.play();
}
async init() {
await this.#shape?.init();
}
pause() {
if (this.#paused) {
return;
}
this.#emitDelay = undefined;
}
play() {
if (this.#paused) {
return;
}
if (!((this.#lifeCount > minLifeCount$1 || this.#immortal || !this.options.life.count) &&
(this.#firstSpawn || this.#currentSpawnDelay >= (this.#spawnDelay ?? defaultSpawnDelay$1)))) {
return;
}
const container = this.#container;
if (this.#emitDelay === undefined) {
const delay = getRangeValue(this.options.rate.delay);
this.#emitDelay = container.retina.reduceFactor
? (delay * millisecondsToSeconds$1) / container.retina.reduceFactor
: Infinity;
}
if (this.#lifeCount > minLifeCount$1 || this.#immortal) {
this.#prepareToDie();
}
}
resize() {
const initialPosition = this.#initialPosition, container = this.#container;
this.position =
initialPosition && isPointInside(initialPosition, container.canvas.size, Vector.origin)
? initialPosition
: this.#calcPosition();
this.#size = this.#calcSize();
this.size =
this.#size.mode === exports.PixelMode.percent
? {
width: (this.#size.width / percentDenominator) * container.canvas.size.width,
height: (this.#size.height / percentDenominator) * container.canvas.size.height,
}
: { width: this.#size.width, height: this.#size.height };
this.#shape?.resize(this.position, this.size);
}
update(delta) {
if (this.#paused) {
return;
}
const container = this.#container;
if (this.#firstSpawn) {
this.#firstSpawn = false;
this.#currentSpawnDelay = this.#spawnDelay ?? defaultSpawnDelay$1;
this.#currentEmitDelay = this.#emitDelay ?? defaultEmitDelay;
}
if (!this.#startParticlesAdded) {
this.#startParticlesAdded = true;
this.#emitParticles(this.options.startCount);
}
if (this.#duration !== undefined) {
this.#currentDuration += delta.value;
if (this.#currentDuration >= this.#duration) {
this.pause();
if (this.#spawnDelay !== undefined) {
this.#spawnDelay = undefined;
}
if (!this.#immortal) {
this.#lifeCount--;
}
if (this.#lifeCount > minLifeCount$1 || this.#immortal) {
this.position = this.#calcPosition();
this.#shape?.resize(this.position, this.size);
this.#spawnDelay = container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay$1) * millisecondsToSeconds$1) /
container.retina.reduceFactor
: Infinity;
}
else {
this.#destroy();
}
this.#currentDuration -= this.#duration;
this.#duration = undefined;
}
}
if (this.#spawnDelay !== undefined) {
this.#currentSpawnDelay += delta.value;
if (this.#currentSpawnDelay >= this.#spawnDelay) {
this.#container.dispatchEvent("emitterPlay");
this.play();
this.#currentSpawnDelay -= this.#spawnDelay;
this.#spawnDelay = undefined;
}
}
if (this.#emitDelay !== undefined) {
this.#currentEmitDelay += delta.value;
if (this.#currentEmitDelay >= this.#emitDelay) {
this.#emit();
this.#currentEmitDelay -= this.#emitDelay;
}
}
}
#calcPosition() {
const container = this.#container;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
const elRect = element.getBoundingClientRect(), pxRatio = container.retina.pixelRatio;
return {
x: (elRect.x + elRect.width * half$2) * pxRatio,
y: (elRect.y + elRect.height * half$2) * pxRatio,
};
}
}
return calcPositionOrRandomFromSizeRanged({
size: container.canvas.size,
position: this.options.position,
});
}
#calcSize() {
const container = this.#container;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
const elRect = element.getBoundingClientRect();
return {
width: elRect.width * container.retina.pixelRatio,
height: elRect.height * container.retina.pixelRatio,
mode: exports.PixelMode.precise,
};
}
}
return (this.options.size ??
(() => {
const size = new EmitterSize();
size.load({
height: 0,
mode: exports.PixelMode.percent,
width: 0,
});
return size;
})());
}
#destroy() {
this.#mutationObserver?.disconnect();
this.#mutationObserver = undefined;
this.#resizeObserver?.disconnect();
this.#resizeObserver = undefined;
this.#removeCallback(this);
this.#container.dispatchEvent("emitterDestroyed", {
emitter: this,
});
}
#emit() {
if (this.#paused) {
return;
}
const quantity = getRangeValue(this.options.rate.quantity);
this.#emitParticles(quantity);
}
#emitParticles(quantity) {
const singleParticlesOptions = (itemFromSingleOrMultiple(this.#particlesOptions) ??
{}), fillHslAnimation = this.options.spawn.fill?.color?.animation, fillEnabled = this.options.spawn.fill?.enable ?? !!this.options.spawn.fill?.color, fillOpacity = this.options.spawn.fill?.opacity === undefined
? defaultOpacity$3
: getRangeValue(this.options.spawn.fill.opacity), strokeHslAnimation = this.options.spawn.stroke?.color?.animation, strokeOpacity = this.options.spawn.stroke?.opacity === undefined
? defaultOpacity$3
: getRangeValue(this.options.spawn.stroke.opacity), strokeWidth = this.options.spawn.stroke?.width === undefined
? defaultStrokeWidth
: getRangeValue(this.options.spawn.stroke.width), reduceFactor = this.#container.retina.reduceFactor, needsFillColorAnimation = !!fillHslAnimation, needsStrokeColorAnimation = !!strokeHslAnimation, needsShapeData = !!this.#shape, needsColorAnimation = needsFillColorAnimation || needsStrokeColorAnimation, needsCopy = needsColorAnimation || needsShapeData, maxValues = needsColorAnimation ? { h: hMax, s: sMax, l: lMax } : null, shapeOptions = this.options.shape;
for (let i = 0; i < quantity * reduceFactor; i++) {
const particlesOptions = needsCopy
? deepExtend({}, singleParticlesOptions)
: singleParticlesOptions;
this.spawnFillOpacity = fillOpacity;
this.spawnFillEnabled = fillEnabled;
this.spawnStrokeOpacity = strokeOpacity;
this.spawnStrokeWidth = strokeWidth;
if (this.spawnFillColor) {
if (fillHslAnimation && maxValues) {
this.spawnFillColor.h = this.#setColorAnimation(fillHslAnimation.h, this.spawnFillColor.h, maxValues.h, colorFactor);
this.spawnFillColor.s = this.#setColorAnimation(fillHslAnimation.s, this.spawnFillColor.s, maxValues.s);
this.spawnFillColor.l = this.#setColorAnimation(fillHslAnimation.l, this.spawnFillColor.l, maxValues.l);
}
setParticlesOptionsFillColor(particlesOptions, this.spawnFillColor, this.spawnFillOpacity, this.spawnFillEnabled);
}
if (this.spawnStrokeColor) {
if (strokeHslAnimation && maxValues) {
this.spawnStrokeColor.h = this.#setColorAnimation(strokeHslAnimation.h, this.spawnStrokeColor.h, maxValues.h, colorFactor);
this.spawnStrokeColor.s = this.#setColorAnimation(strokeHslAnimation.s, this.spawnStrokeColor.s, maxValues.s);
this.spawnStrokeColor.l = this.#setColorAnimation(strokeHslAnimation.l, this.spawnStrokeColor.l, maxValues.l);
}
setParticlesOptionsStrokeColor(particlesOptions, this.spawnStrokeColor, this.spawnStrokeOpacity, this.spawnStrokeWidth);
}
let position = this.position;
if (this.#shape) {
const shapePosData = this.#shape.randomPosition();
if (shapePosData) {
position = shapePosData.position;
const replaceData = shapeOptions.replace;
if (replaceData.color && shapePosData.color) {
setParticlesOptionsFillColor(particlesOptions, shapePosData.color, replaceData.opacity ? (shapePosData.opacity ?? defaultOpacity$3) : defaultOpacity$3, true);
}
}
else {
position = null;
}
}
if (position) {
this.#container.particles.addParticle(position, particlesOptions);
}
}
}
#prepareToDie() {
if (this.#paused) {
return;
}
const duration = this.options.life.duration !== undefined ? getRangeValue(this.options.life.duration) : undefined, minDuration = 0, minLifeCount = 0;
if ((this.#lifeCount > minLifeCount || this.#immortal) && duration !== undefined && duration > minDuration) {
this.#duration = duration * millisecondsToSeconds$1;
}
}
#setColorAnimation(animation, initValue, maxValue, factor = defaultColorAnimationFactor) {
const container = this.#container;
if (!animation.enable) {
return initValue;
}
const colorOffset = randomInRangeValue(animation.offset), delay = getRangeValue(this.options.rate.delay), emitFactor = container.retina.reduceFactor
? (delay * millisecondsToSeconds$1) / container.retina.reduceFactor
: Infinity, colorSpeed = getRangeValue(animation.speed);
return (initValue + (colorSpeed * container.fpsLimit) / emitFactor + colorOffset * factor) % maxValue;
}
}
var EmitterInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmitterInstance: EmitterInstance
});
const absorbFactor = 0.033, minOrbitLength = 0, minRadius = 0, minMass = 0, minAngle = 0, maxAngle = doublePI, maxDegreeAngle = 360, angleIncrementFactor = identity$3 / maxDegreeAngle, defaultLifeDelay = 0, minLifeCount = 0, defaultSpawnDelay = 0, defaultLifeCount = -1;
class AbsorberInstance {
color;
limit;
mass;
name;
opacity;
options;
position;
size;
#container;
#currentDuration;
#currentSpawnDelay;
#duration;
#firstSpawn;
#immortal;
#initialPosition;
#lifeCount;
#pluginManager;
#spawnDelay;
constructor(pluginManager, container, options, position) {
this.#container = container;
this.#pluginManager = pluginManager;
this.#currentDuration = 0;
this.#currentSpawnDelay = 0;
this.#initialPosition = position ? Vector.create(position.x, position.y) : undefined;
if (options instanceof Absorber) {
this.options = options;
}
else {
this.options = new Absorber();
this.options.load(options);
}
this.name = this.options.name;
this.opacity = this.options.opacity;
this.size = getRangeValue(this.options.size.value) * container.retina.pixelRatio;
this.mass = this.size * this.options.size.density * container.retina.reduceFactor;
const limit = this.options.size.limit;
this.limit = {
radius: limit.radius * container.retina.pixelRatio * container.retina.reduceFactor,
mass: limit.mass,
};
this.color = rangeColorToRgb(this.#pluginManager, this.options.color) ?? {
b: 0,
g: 0,
r: 0,
};
this.position = this.#initialPosition?.copy() ?? this.#calcPosition();
this.#firstSpawn = !this.options.life.wait;
this.#lifeCount = this.options.life.count ?? defaultLifeCount;
this.#immortal = this.#lifeCount <= minLifeCount;
this.#spawnDelay = container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay) * millisecondsToSeconds$1) /
container.retina.reduceFactor
: Infinity;
}
attract(particle, delta) {
const container = this.#container, options = this.options, pos = particle.getPosition(), { dx, dy, distance } = getDistances(this.position, pos), v = Vector.create(dx, dy);
v.length = (this.mass / Math.pow(distance, squareExp)) * container.retina.reduceFactor;
if (distance < this.size + particle.getRadius()) {
const sizeFactor = particle.getRadius() * absorbFactor * container.retina.pixelRatio * delta.factor;
if ((this.size > particle.getRadius() && distance < this.size - particle.getRadius()) ||
(particle.absorberOrbit !== undefined && particle.absorberOrbit.length < minOrbitLength)) {
if (options.destroy) {
particle.destroy();
}
else {
particle.needsNewPosition = true;
this.#updateParticlePosition(particle, delta, v);
}
}
else {
if (options.destroy) {
particle.size.value -= sizeFactor;
}
this.#updateParticlePosition(particle, delta, v);
}
if (this.limit.radius <= minRadius || this.size < this.limit.radius) {
this.size += sizeFactor;
}
if (this.limit.mass <= minMass || this.mass < this.limit.mass) {
this.mass += sizeFactor * this.options.size.density * container.retina.reduceFactor;
}
}
else {
this.#updateParticlePosition(particle, delta, v);
}
}
draw(context) {
context.translate(this.position.x, this.position.y);
context.beginPath();
context.arc(originPoint.x, originPoint.y, this.size, minAngle, maxAngle, false);
context.closePath();
context.fillStyle = getStyleFromRgb(this.color, this.#container.hdr, this.opacity, this.#container.peakNits, this.#container.hdrMode);
context.fill();
}
resize() {
const initialPosition = this.#initialPosition;
this.position =
initialPosition && isPointInside(initialPosition, this.#container.canvas.size, Vector.origin)
? initialPosition
: this.#calcPosition();
}
update(delta) {
if (this.#firstSpawn) {
this.#firstSpawn = false;
this.#currentSpawnDelay = this.#spawnDelay ?? defaultSpawnDelay;
}
if (this.#duration !== undefined) {
this.#currentDuration += delta.value;
if (this.#currentDuration >= this.#duration) {
if (!this.#immortal) {
this.#lifeCount--;
}
if (this.#lifeCount > minLifeCount || this.#immortal) {
this.position = this.#calcPosition();
this.#spawnDelay = this.#container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay) * millisecondsToSeconds$1) /
this.#container.retina.reduceFactor
: Infinity;
}
this.#currentDuration -= this.#duration;
this.#duration = undefined;
}
}
if (this.#spawnDelay !== undefined) {
this.#currentSpawnDelay += delta.value;
if (this.#currentSpawnDelay >= this.#spawnDelay) {
this.#play();
this.#currentSpawnDelay -= this.#spawnDelay;
this.#spawnDelay = undefined;
}
}
}
#calcPosition() {
const exactPosition = calcPositionOrRandomFromSizeRanged({
size: this.#container.canvas.size,
position: this.options.position,
});
return Vector.create(exactPosition.x, exactPosition.y);
}
#play() {
if (!((this.#lifeCount > minLifeCount || this.#immortal || !this.options.life.count) &&
(this.#firstSpawn || this.#currentSpawnDelay >= (this.#spawnDelay ?? defaultSpawnDelay)))) {
return;
}
if (this.#lifeCount > minLifeCount || this.#immortal) {
this.#prepareToDie();
}
}
#prepareToDie() {
const duration = this.options.life.duration !== undefined ? getRangeValue(this.options.life.duration) : undefined, minDuration = 0;
if ((this.#lifeCount > minLifeCount || this.#immortal) && duration !== undefined && duration > minDuration) {
this.#duration = duration * millisecondsToSeconds$1;
}
}
#updateParticlePosition(particle, delta, v) {
if (particle.destroyed) {
return;
}
const container = this.#container, canvasSize = container.canvas.size;
if (particle.needsNewPosition) {
const newPosition = calcPositionOrRandomFromSize({ size: canvasSize });
particle.position.setTo(newPosition);
particle.velocity.setTo(particle.initialVelocity);
particle.absorberOrbit = undefined;
particle.needsNewPosition = false;
}
if (this.options.orbits) {
if (particle.absorberOrbit === undefined) {
particle.absorberOrbit = Vector.origin;
particle.absorberOrbit.length = getDistance(particle.getPosition(), this.position);
particle.absorberOrbit.angle = getRandom() * maxAngle;
}
if (particle.absorberOrbit.length <= this.size && !this.options.destroy) {
const minSize = Math.min(canvasSize.width, canvasSize.height), offset = 1, randomOffset = 0.1, randomFactor = 0.2;
particle.absorberOrbit.length = minSize * (offset + (getRandom() * randomFactor - randomOffset));
}
particle.absorberOrbitDirection ??=
particle.velocity.x >= minVelocity ? exports.RotateDirection.clockwise : exports.RotateDirection.counterClockwise;
const orbitRadius = particle.absorberOrbit.length, orbitAngle = particle.absorberOrbit.angle, orbitDirection = particle.absorberOrbitDirection;
particle.velocity.setTo(Vector.origin);
const maxSize = particle.size.max, sizeFactor = particle.options.move.size ? particle.getRadius() / maxSize : identity$3, deltaFactor = delta.factor || identity$3, baseSpeed = particle.retina.moveSpeed, moveSpeed = baseSpeed * sizeFactor * deltaFactor * half$2;
particle.position.x = this.position.x + orbitRadius * Math.cos(orbitAngle);
particle.position.y =
this.position.y +
orbitRadius * (orbitDirection === exports.RotateDirection.clockwise ? identity$3 : -identity$3) * Math.sin(orbitAngle);
particle.absorberOrbit.length = Math.max(minOrbitLength, particle.absorberOrbit.length - v.length);
particle.absorberOrbit.angle += moveSpeed * angleIncrementFactor * container.retina.reduceFactor;
}
else {
particle.velocity.addTo(v);
}
}
}
var AbsorberInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
AbsorberInstance: AbsorberInstance
});
class PoissonDisc {
active;
cellSize;
cols;
dimensions;
firstPoint;
grid;
points;
radius;
retries;
rows;
size;
constructor(size, radius, retries, dimensions, firstPoint) {
this.size = { ...size };
this.radius = radius;
this.retries = retries;
this.dimensions = dimensions;
this.cellSize = Math.floor(this.radius / Math.sqrt(this.dimensions));
this.cols = Math.floor(this.size.width / this.cellSize);
this.rows = Math.floor(this.size.height / this.cellSize);
this.points = [];
this.active = [];
this.grid = [];
this.firstPoint = firstPoint ? { ...firstPoint } : undefined;
this.reset();
}
addPoint(inputPoint) {
const point = {
position: { ...inputPoint },
gridPosition: {
x: Math.floor(inputPoint.x / this.cellSize),
y: Math.floor(inputPoint.y / this.cellSize),
},
}, pointIndex = this.points.length, row = this.grid[point.gridPosition.y];
if (!row) {
return;
}
this.points.push(point);
row[point.gridPosition.x] = pointIndex;
this.active.push(pointIndex);
}
getRandom(min, max) {
return Math.floor(getRandom() * (max - min)) + min;
}
initialiseGrid() {
for (let y = 0; y <= this.rows; y++) {
this.grid[y] = [];
const row = this.grid[y];
if (!row) {
continue;
}
for (let x = 0; x <= this.cols; x++) {
row[x] = -1;
}
}
}
reset() {
this.points = [];
this.active = [];
this.grid = [];
this.initialiseGrid();
if (this.firstPoint) {
this.addPoint(this.firstPoint);
}
else {
const minCoordinate = 0;
this.addPoint({
x: this.getRandom(minCoordinate, this.size.width),
y: this.getRandom(minCoordinate, this.size.height),
});
}
}
async run() {
this.reset();
const minCount = 0, step = 1, yieldEvery = 100, yieldStepModule = 0;
let iterations = 0;
while (this.active.length > minCount) {
this.steps(step);
if (++iterations % yieldEvery === yieldStepModule) {
await new Promise(resolve => setTimeout(resolve));
}
}
}
steps(steps) {
const minCount = 0;
for (let i = 0; i < steps; i++) {
if (this.active.length <= minCount) {
continue;
}
this.#step();
}
}
#getNewPoint(currentPoint, tries) {
const minCoordinate = 0, gridMinValue = 0, maxNeighbourIndex = 1, newAngle = tries * (doublePI / this.retries), newDist = this.getRandom(this.radius, this.radius * double), offset = {
x: Math.cos(newAngle) * newDist,
y: Math.sin(newAngle) * newDist,
}, newPoint = {
x: Math.floor(currentPoint.position.x + offset.x),
y: Math.floor(currentPoint.position.y + offset.y),
}, newGridCoords = {
x: Math.floor(newPoint.x / this.cellSize),
y: Math.floor(newPoint.y / this.cellSize),
};
if (newPoint.x <= minCoordinate ||
newPoint.x >= this.size.width ||
newPoint.y <= minCoordinate ||
newPoint.y >= this.size.height) {
return;
}
const row = this.grid[newGridCoords.y];
if (!row) {
return;
}
const cellValue = row[newGridCoords.x];
if (cellValue === undefined) {
return;
}
if (cellValue >= gridMinValue) {
return;
}
for (let i = -1; i <= maxNeighbourIndex; i++) {
for (let j = -1; j <= maxNeighbourIndex; j++) {
if (!i && !j) {
continue;
}
const neighbourGrid = {
x: newGridCoords.x + j,
y: newGridCoords.y + i,
};
if (neighbourGrid.x < minCoordinate ||
neighbourGrid.y < minCoordinate ||
neighbourGrid.x >= this.cols ||
neighbourGrid.y >= this.rows) {
continue;
}
const neighbourCellValue = this.grid[neighbourGrid.y]?.[neighbourGrid.x];
if (neighbourCellValue === undefined || neighbourCellValue < gridMinValue) {
continue;
}
const neighbour = this.points[neighbourCellValue];
if (!neighbour) {
continue;
}
if (getDistance(newPoint, neighbour.position) < this.radius) {
return;
}
}
}
return newPoint;
}
#step() {
const minCount = 0, randomActive = this.getRandom(minCount, this.active.length);
let foundNewPoint = false;
for (let tries = 0; tries < this.retries; tries++) {
const randomActivePointIndex = this.active[randomActive];
if (randomActivePointIndex === undefined) {
continue;
}
const point = this.points[randomActivePointIndex];
if (!point) {
continue;
}
const newPoint = this.#getNewPoint(point, tries);
if (newPoint) {
foundNewPoint = true;
this.addPoint(newPoint);
break;
}
}
if (!foundNewPoint) {
const deleteCount = 1;
this.active.splice(randomActive, deleteCount);
}
}
}
var PoissonDisc$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
PoissonDisc: PoissonDisc
});
exports.AnimatableColor = AnimatableColor;
exports.AnimationOptions = AnimationOptions;
exports.AnimationValueWithRandom = AnimationValueWithRandom;
exports.Background = Background;
exports.BaseRange = BaseRange;
exports.Circle = Circle;
exports.ColorAnimation = ColorAnimation;
exports.Fill = Fill;
exports.FullScreen = FullScreen;
exports.HDROptions = HDROptions;
exports.HslAnimation = HslAnimation;
exports.Move = Move;
exports.MoveAngle = MoveAngle;
exports.MoveCenter = MoveCenter;
exports.MoveGravity = MoveGravity;
exports.MovePath = MovePath;
exports.OptionLoader = OptionLoader;
exports.Options = Options;
exports.OptionsColor = OptionsColor;
exports.OutModes = OutModes;
exports.Paint = Paint;
exports.ParticlesBounce = ParticlesBounce;
exports.ParticlesBounceFactor = ParticlesBounceFactor;
exports.ParticlesDensity = ParticlesDensity;
exports.ParticlesNumber = ParticlesNumber;
exports.ParticlesNumberLimit = ParticlesNumberLimit;
exports.ParticlesOptions = ParticlesOptions;
exports.RangedAnimationOptions = RangedAnimationOptions;
exports.RangedAnimationValueWithRandom = RangedAnimationValueWithRandom;
exports.Rectangle = Rectangle;
exports.ResizeEvent = ResizeEvent;
exports.Shape = Shape;
exports.Spin = Spin;
exports.Stroke = Stroke;
exports.ValueWithRandom = ValueWithRandom;
exports.Vector = Vector;
exports.Vector3d = Vector3d;
exports.ZIndex = ZIndex;
exports.alterHsl = alterHsl;
exports.animate = animate;
exports.areBoundsInside = areBoundsInside;
exports.calcExactPositionOrRandomFromSize = calcExactPositionOrRandomFromSize;
exports.calcPositionFromSize = calcPositionFromSize;
exports.calcPositionOrRandomFromSize = calcPositionOrRandomFromSize;
exports.calcPositionOrRandomFromSizeRanged = calcPositionOrRandomFromSizeRanged;
exports.calculateBounds = calculateBounds;
exports.cancelAnimation = cancelAnimation;
exports.canvasFirstIndex = canvasFirstIndex;
exports.canvasTag = canvasTag;
exports.checkDistance = checkDistance;
exports.circleBounce = circleBounce;
exports.circleBounceDataFromParticle = circleBounceDataFromParticle;
exports.clamp = clamp;
exports.cloneStyle = cloneStyle;
exports.collisionVelocity = collisionVelocity;
exports.colorMix = colorMix;
exports.colorToRgb = colorToRgb;
exports.countOffset = countOffset;
exports.decayOffset = decayOffset;
exports.deepExtend = deepExtend;
exports.defaultAlpha = defaultAlpha$1;
exports.defaultAngle = defaultAngle$1;
exports.defaultCompositeValue = defaultCompositeValue;
exports.defaultDensityFactor = defaultDensityFactor;
exports.defaultFps = defaultFps;
exports.defaultFpsLimit = defaultFpsLimit;
exports.defaultLoops = defaultLoops;
exports.defaultOpacity = defaultOpacity$3;
exports.defaultRatio = defaultRatio$1;
exports.defaultReduceFactor = defaultReduceFactor;
exports.defaultRemoveQuantity = defaultRemoveQuantity;
exports.defaultRetryCount = defaultRetryCount;
exports.defaultRgbMin = defaultRgbMin;
exports.defaultTime = defaultTime;
exports.defaultTransform = defaultTransform$1;
exports.defaultTransformValue = defaultTransformValue;
exports.defaultVelocity = defaultVelocity;
exports.defaultZoom = defaultZoom;
exports.degToRad = degToRad;
exports.deleteCount = deleteCount;
exports.double = double;
exports.doublePI = doublePI;
exports.empty = empty$1;
exports.executeOnSingleOrMultiple = executeOnSingleOrMultiple;
exports.generatedAttribute = generatedAttribute;
exports.generatedFalse = generatedFalse;
exports.generatedTrue = generatedTrue;
exports.getDistance = getDistance;
exports.getDistanceSq = getDistanceSq;
exports.getDistances = getDistances;
exports.getFullScreenStyle = getFullScreenStyle;
exports.getHslAnimationFromHsl = getHslAnimationFromHsl;
exports.getHslFromAnimation = getHslFromAnimation;
exports.getItemMapFromInitializer = getItemMapFromInitializer;
exports.getItemsFromInitializer = getItemsFromInitializer;
exports.getLinkColor = getLinkColor;
exports.getLinkRandomColor = getLinkRandomColor;
exports.getLogger = getLogger;
exports.getParticleBaseVelocity = getParticleBaseVelocity;
exports.getParticleDirectionAngle = getParticleDirectionAngle;
exports.getPosition = getPosition;
exports.getRandom = getRandom;
exports.getRandomFn = getRandomFn;
exports.getRandomInRange = getRandomInRange;
exports.getRandomRgbColor = getRandomRgbColor;
exports.getRangeMax = getRangeMax;
exports.getRangeMin = getRangeMin;
exports.getRangeValue = getRangeValue;
exports.getStyleFromHsl = getStyleFromHsl;
exports.getStyleFromRgb = getStyleFromRgb;
exports.hMax = hMax;
exports.hMin = hMin;
exports.hPhase = hPhase;
exports.half = half$2;
exports.hslToRgb = hslToRgb;
exports.hslToRgbFloat = hslToRgbFloat;
exports.hslaToRgba = hslaToRgba;
exports.identity = identity$3;
exports.inverseFactorNumerator = inverseFactorNumerator;
exports.isArray = isArray;
exports.isBoolean = isBoolean;
exports.isFunction = isFunction;
exports.isInArray = isInArray;
exports.isNull = isNull;
exports.isNumber = isNumber;
exports.isObject = isObject;
exports.isPointInside = isPointInside;
exports.isString = isString;
exports.itemFromArray = itemFromArray;
exports.itemFromSingleOrMultiple = itemFromSingleOrMultiple;
exports.lFactor = lFactor;
exports.lMax = lMax;
exports.lMin = lMin;
exports.lengthOffset = lengthOffset;
exports.loadAll = loadAll;
exports.loadExtendProperty = loadExtendProperty;
exports.loadLazyProperty = loadLazyProperty;
exports.loadMinIndex = loadMinIndex;
exports.loadNestedProperty = loadNestedProperty;
exports.loadOptionProperty = loadOptionProperty;
exports.loadOptions = loadOptions;
exports.loadParticlesOptions = loadParticlesOptions;
exports.loadProperty = loadProperty;
exports.loadRandomFactor = loadRandomFactor;
exports.loadRangeProperty = loadRangeProperty;
exports.manageListener = manageListener;
exports.maxNits = maxNits;
exports.midColorValue = midColorValue;
exports.millisecondsToSeconds = millisecondsToSeconds$1;
exports.minCount = minCount;
exports.minDistance = minDistance$7;
exports.minFpsLimit = minFpsLimit;
exports.minIndex = minIndex$1;
exports.minLimit = minLimit;
exports.minRadius = minRadius$8;
exports.minStrokeWidth = minStrokeWidth;
exports.minVelocity = minVelocity;
exports.minZ = minZ;
exports.minimumSize = minimumSize;
exports.mix = mix;
exports.none = none;
exports.one = one;
exports.originPoint = originPoint;
exports.parseAlpha = parseAlpha;
exports.percentDenominator = percentDenominator;
exports.phaseNumerator = phaseNumerator;
exports.quarter = quarter$2;
exports.randomColorValue = randomColorValue;
exports.randomInRangeValue = randomInRangeValue;
exports.rangeColorToHsl = rangeColorToHsl;
exports.rangeColorToRgb = rangeColorToRgb;
exports.removeDeleteCount = removeDeleteCount;
exports.removeMinIndex = removeMinIndex;
exports.resizeEvent = resizeEvent;
exports.rgbMax = rgbMax;
exports.rgbToHsl = rgbToHsl;
exports.sMax = sMax;
exports.sMin = sMin;
exports.sNormalizedOffset = sNormalizedOffset;
exports.safeDocument = safeDocument;
exports.safeMatchMedia = safeMatchMedia;
exports.safeMutationObserver = safeMutationObserver;
exports.setAnimationFunctions = setAnimationFunctions;
exports.setLogger = setLogger;
exports.setRandom = setRandom;
exports.setRangeValue = setRangeValue;
exports.sextuple = sextuple;
exports.spatialHashGridCellSize = spatialHashGridCellSize;
exports.squareExp = squareExp;
exports.stringToAlpha = stringToAlpha;
exports.stringToRgb = stringToRgb;
exports.threeQuarter = threeQuarter;
exports.triple = triple;
exports.tryCountIncrement = tryCountIncrement;
exports.tsParticles = tsParticles;
exports.updateColor = updateColor;
exports.updateColorValue = updateColorValue;
exports.visibilityChangeEvent = visibilityChangeEvent;
exports.zIndexFactorOffset = zIndexFactorOffset;
}));
Object.assign(globalThis.window || globalThis, { loadAll: (globalThis.__tsParticlesInternals.bundles.all || {}).loadAll, loadExtendProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadExtendProperty, loadLazyProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadLazyProperty, loadMinIndex: (globalThis.__tsParticlesInternals.bundles.all || {}).loadMinIndex, loadNestedProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadNestedProperty, loadOptionProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadOptionProperty, loadOptions: (globalThis.__tsParticlesInternals.bundles.all || {}).loadOptions, loadParticlesOptions: (globalThis.__tsParticlesInternals.bundles.all || {}).loadParticlesOptions, loadProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadProperty, loadRandomFactor: (globalThis.__tsParticlesInternals.bundles.all || {}).loadRandomFactor, loadRangeProperty: (globalThis.__tsParticlesInternals.bundles.all || {}).loadRangeProperty, tsParticles: (globalThis.__tsParticlesInternals.bundles.all || {}).tsParticles });
globalThis.__tsParticlesInternals = globalThis.__tsParticlesInternals || {};
if (!globalThis.__tsParticlesInternals.engine || !globalThis.__tsParticlesInternals.engine.tsParticles) {
globalThis.__tsParticlesInternals.engine = globalThis.__tsParticlesInternals.bundles.all || {};
}
delete (globalThis.window || globalThis).tsparticlesInternalExports;