import { collisionVelocity, getDistances, getRandom, getValue } from "./NumberUtils"; import { Vector } from "../Core/Utils/Vector"; function rectSideBounce(pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor) { const res = { bounced: false }; 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) / 2 && velocity > 0) || (pSide.min <= rectSide.max && pSide.min > (rectSide.max + rectSide.min) / 2 && velocity < 0)) { res.velocity = velocity * -factor; res.bounced = true; } return res; } function checkSelector(element, selectors) { const res = executeOnSingleOrMultiple(selectors, (selector) => { return element.matches(selector); }); return res instanceof Array ? res.some((t) => t) : res; } export function isSsr() { return typeof window === "undefined" || !window || typeof window.document === "undefined" || !window.document; } export function hasMatchMedia() { return !isSsr() && typeof matchMedia !== "undefined"; } export function safeMatchMedia(query) { if (!hasMatchMedia()) { return; } return matchMedia(query); } export function animate() { return isSsr() ? (callback) => setTimeout(callback) : (callback) => (requestAnimationFrame || setTimeout)(callback); } export function cancelAnimation() { return isSsr() ? (handle) => clearTimeout(handle) : (handle) => (cancelAnimationFrame || clearTimeout)(handle); } export function isInArray(value, array) { return value === array || (array instanceof Array && array.indexOf(value) > -1); } export async function loadFont(font, weight) { try { await document.fonts.load(`${weight !== null && weight !== void 0 ? weight : "400"} 36px '${font !== null && font !== void 0 ? font : "Verdana"}'`); } catch (_a) { } } export function arrayRandomIndex(array) { return Math.floor(getRandom() * array.length); } export function itemFromArray(array, index, useIndex = true) { return array[index !== undefined && useIndex ? index % array.length : arrayRandomIndex(array)]; } export function isPointInside(point, size, offset, radius, direction) { return areBoundsInside(calculateBounds(point, radius !== null && radius !== void 0 ? radius : 0), size, offset, direction); } export function areBoundsInside(bounds, size, offset, direction) { let inside = true; if (!direction || direction === "bottom") { inside = bounds.top < size.height + offset.x; } if (inside && (!direction || direction === "left")) { inside = bounds.right > offset.x; } if (inside && (!direction || direction === "right")) { inside = bounds.left < size.width + offset.y; } if (inside && (!direction || direction === "top")) { inside = bounds.bottom > offset.y; } return inside; } export function calculateBounds(point, radius) { return { bottom: point.y + radius, left: point.x - radius, right: point.x + radius, top: point.y - radius, }; } export function deepExtend(destination, ...sources) { for (const source of sources) { if (source === undefined || source === null) { continue; } if (typeof source !== "object") { destination = source; continue; } const sourceIsArray = Array.isArray(source); if (sourceIsArray && (typeof destination !== "object" || !destination || !Array.isArray(destination))) { destination = []; } else if (!sourceIsArray && (typeof destination !== "object" || !destination || Array.isArray(destination))) { destination = {}; } for (const key in source) { if (key === "__proto__") { continue; } const sourceDict = source, value = sourceDict[key], isObject = typeof value === "object", destDict = destination; destDict[key] = isObject && Array.isArray(value) ? value.map((v) => deepExtend(destDict[key], v)) : deepExtend(destDict[key], value); } } return destination; } export function isDivModeEnabled(mode, divs) { return !!findItemFromSingleOrMultiple(divs, (t) => t.enable && isInArray(mode, t.mode)); } export function divModeExecute(mode, divs, callback) { executeOnSingleOrMultiple(divs, (div) => { const divMode = div.mode, divEnabled = div.enable; if (divEnabled && isInArray(mode, divMode)) { singleDivModeExecute(div, callback); } }); } export function singleDivModeExecute(div, callback) { const selectors = div.selectors; executeOnSingleOrMultiple(selectors, (selector) => { callback(selector, div); }); } export function divMode(divs, element) { if (!element || !divs) { return; } return findItemFromSingleOrMultiple(divs, (div) => { return checkSelector(element, div.selectors); }); } export function circleBounceDataFromParticle(p) { return { position: p.getPosition(), radius: p.getRadius(), mass: p.getMass(), velocity: p.velocity, factor: Vector.create(getValue(p.options.bounce.horizontal), getValue(p.options.bounce.vertical)), }; } export 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); if (xVelocityDiff * xDist + yVelocityDiff * yDist < 0) { 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; } export function rectBounce(particle, divBounds) { const pPos = particle.getPosition(), size = particle.getRadius(), bounds = calculateBounds(pPos, size), resH = rectSideBounce({ min: bounds.left, max: bounds.right, }, { min: bounds.top, max: bounds.bottom, }, { min: divBounds.left, max: divBounds.right, }, { min: divBounds.top, max: divBounds.bottom, }, particle.velocity.x, getValue(particle.options.bounce.horizontal)); if (resH.bounced) { if (resH.velocity !== undefined) { particle.velocity.x = resH.velocity; } if (resH.position !== undefined) { particle.position.x = resH.position; } } const resV = rectSideBounce({ min: bounds.top, max: bounds.bottom, }, { min: bounds.left, max: bounds.right, }, { min: divBounds.top, max: divBounds.bottom, }, { min: divBounds.left, max: divBounds.right, }, particle.velocity.y, getValue(particle.options.bounce.vertical)); if (resV.bounced) { if (resV.velocity !== undefined) { particle.velocity.y = resV.velocity; } if (resV.position !== undefined) { particle.position.y = resV.position; } } } export function executeOnSingleOrMultiple(obj, callback) { return obj instanceof Array ? obj.map((item, index) => callback(item, index)) : callback(obj, 0); } export function itemFromSingleOrMultiple(obj, index, useIndex) { return obj instanceof Array ? itemFromArray(obj, index, useIndex) : obj; } export function findItemFromSingleOrMultiple(obj, callback) { return obj instanceof Array ? obj.find((t, index) => callback(t, index)) : callback(obj, 0) ? obj : undefined; }