1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- import { Observable } from "./observable.js";
- import { GetDOMTextContent, IsNavigatorAvailable, IsWindowObjectExist } from "./domManagement.js";
- import { Logger } from "./logger.js";
- import { DeepCopier } from "./deepCopier.js";
- import { PrecisionDate } from "./precisionDate.js";
- import { _WarnImport } from "./devTools.js";
- import { WebRequest } from "./webRequest.js";
- import { EngineStore } from "../Engines/engineStore.js";
- import { FileToolsOptions, DecodeBase64UrlToBinary, IsBase64DataUrl, LoadFile as FileToolsLoadFile, LoadImage as FileToolLoadImage, ReadFile as FileToolsReadFile, SetCorsBehavior, } from "./fileTools.js";
- import { TimingTools } from "./timingTools.js";
- import { InstantiationTools } from "./instantiationTools.js";
- import { RandomGUID } from "./guid.js";
- import { IsExponentOfTwo, Mix } from "./tools.functions.js";
- /**
- * Class containing a set of static utilities functions
- */
- export class Tools {
- /**
- * Gets or sets the base URL to use to load assets
- */
- static get BaseUrl() {
- return FileToolsOptions.BaseUrl;
- }
- static set BaseUrl(value) {
- FileToolsOptions.BaseUrl = value;
- }
- /**
- * This function checks whether a URL is absolute or not.
- * It will also detect data and blob URLs
- * @param url the url to check
- * @returns is the url absolute or relative
- */
- static IsAbsoluteUrl(url) {
- // See https://stackoverflow.com/a/38979205.
- // URL is protocol-relative (= absolute)
- if (url.indexOf("//") === 0) {
- return true;
- }
- // URL has no protocol (= relative)
- if (url.indexOf("://") === -1) {
- return false;
- }
- // URL does not contain a dot, i.e. no TLD (= relative, possibly REST)
- if (url.indexOf(".") === -1) {
- return false;
- }
- // URL does not contain a single slash (= relative)
- if (url.indexOf("/") === -1) {
- return false;
- }
- // The first colon comes after the first slash (= relative)
- if (url.indexOf(":") > url.indexOf("/")) {
- return false;
- }
- // Protocol is defined before first dot (= absolute)
- if (url.indexOf("://") < url.indexOf(".")) {
- return true;
- }
- if (url.indexOf("data:") === 0 || url.indexOf("blob:") === 0) {
- return true;
- }
- // Anything else must be relative
- return false;
- }
- /**
- * Sets the base URL to use to load scripts
- */
- static set ScriptBaseUrl(value) {
- FileToolsOptions.ScriptBaseUrl = value;
- }
- static get ScriptBaseUrl() {
- return FileToolsOptions.ScriptBaseUrl;
- }
- /**
- * Sets a preprocessing function to run on a source URL before importing it
- * Note that this function will execute AFTER the base URL is appended to the URL
- */
- static set ScriptPreprocessUrl(func) {
- FileToolsOptions.ScriptPreprocessUrl = func;
- }
- static get ScriptPreprocessUrl() {
- return FileToolsOptions.ScriptPreprocessUrl;
- }
- /**
- * Gets or sets the retry strategy to apply when an error happens while loading an asset
- */
- static get DefaultRetryStrategy() {
- return FileToolsOptions.DefaultRetryStrategy;
- }
- static set DefaultRetryStrategy(strategy) {
- FileToolsOptions.DefaultRetryStrategy = strategy;
- }
- /**
- * Default behavior for cors in the application.
- * It can be a string if the expected behavior is identical in the entire app.
- * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
- */
- static get CorsBehavior() {
- return FileToolsOptions.CorsBehavior;
- }
- static set CorsBehavior(value) {
- FileToolsOptions.CorsBehavior = value;
- }
- /**
- * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
- * @ignorenaming
- */
- static get UseFallbackTexture() {
- return EngineStore.UseFallbackTexture;
- }
- static set UseFallbackTexture(value) {
- EngineStore.UseFallbackTexture = value;
- }
- /**
- * Use this object to register external classes like custom textures or material
- * to allow the loaders to instantiate them
- */
- static get RegisteredExternalClasses() {
- return InstantiationTools.RegisteredExternalClasses;
- }
- static set RegisteredExternalClasses(classes) {
- InstantiationTools.RegisteredExternalClasses = classes;
- }
- /**
- * Texture content used if a texture cannot loaded
- * @ignorenaming
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static get fallbackTexture() {
- return EngineStore.FallbackTexture;
- }
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static set fallbackTexture(value) {
- EngineStore.FallbackTexture = value;
- }
- /**
- * Read the content of a byte array at a specified coordinates (taking in account wrapping)
- * @param u defines the coordinate on X axis
- * @param v defines the coordinate on Y axis
- * @param width defines the width of the source data
- * @param height defines the height of the source data
- * @param pixels defines the source byte array
- * @param color defines the output color
- */
- static FetchToRef(u, v, width, height, pixels, color) {
- const wrappedU = (Math.abs(u) * width) % width | 0;
- const wrappedV = (Math.abs(v) * height) % height | 0;
- const position = (wrappedU + wrappedV * width) * 4;
- color.r = pixels[position] / 255;
- color.g = pixels[position + 1] / 255;
- color.b = pixels[position + 2] / 255;
- color.a = pixels[position + 3] / 255;
- }
- /**
- * Interpolates between a and b via alpha
- * @param a The lower value (returned when alpha = 0)
- * @param b The upper value (returned when alpha = 1)
- * @param alpha The interpolation-factor
- * @returns The mixed value
- */
- static Mix(a, b, alpha) {
- return 0;
- }
- /**
- * Tries to instantiate a new object from a given class name
- * @param className defines the class name to instantiate
- * @returns the new object or null if the system was not able to do the instantiation
- */
- static Instantiate(className) {
- return InstantiationTools.Instantiate(className);
- }
- /**
- * Polyfill for setImmediate
- * @param action defines the action to execute after the current execution block
- */
- static SetImmediate(action) {
- TimingTools.SetImmediate(action);
- }
- /**
- * Function indicating if a number is an exponent of 2
- * @param value defines the value to test
- * @returns true if the value is an exponent of 2
- */
- static IsExponentOfTwo(value) {
- return true;
- }
- /**
- * Returns the nearest 32-bit single precision float representation of a Number
- * @param value A Number. If the parameter is of a different type, it will get converted
- * to a number or to NaN if it cannot be converted
- * @returns number
- */
- static FloatRound(value) {
- return Math.fround(value);
- }
- /**
- * Extracts the filename from a path
- * @param path defines the path to use
- * @returns the filename
- */
- static GetFilename(path) {
- const index = path.lastIndexOf("/");
- if (index < 0) {
- return path;
- }
- return path.substring(index + 1);
- }
- /**
- * Extracts the "folder" part of a path (everything before the filename).
- * @param uri The URI to extract the info from
- * @param returnUnchangedIfNoSlash Do not touch the URI if no slashes are present
- * @returns The "folder" part of the path
- */
- static GetFolderPath(uri, returnUnchangedIfNoSlash = false) {
- const index = uri.lastIndexOf("/");
- if (index < 0) {
- if (returnUnchangedIfNoSlash) {
- return uri;
- }
- return "";
- }
- return uri.substring(0, index + 1);
- }
- /**
- * Convert an angle in radians to degrees
- * @param angle defines the angle to convert
- * @returns the angle in degrees
- */
- static ToDegrees(angle) {
- return (angle * 180) / Math.PI;
- }
- /**
- * Convert an angle in degrees to radians
- * @param angle defines the angle to convert
- * @returns the angle in radians
- */
- static ToRadians(angle) {
- return (angle * Math.PI) / 180;
- }
- /**
- * Smooth angle changes (kind of low-pass filter), in particular for device orientation "shaking"
- * Use trigonometric functions to avoid discontinuity (0/360, -180/180)
- * @param previousAngle defines last angle value, in degrees
- * @param newAngle defines new angle value, in degrees
- * @param smoothFactor defines smoothing sensitivity; min 0: no smoothing, max 1: new data ignored
- * @returns the angle in degrees
- */
- static SmoothAngleChange(previousAngle, newAngle, smoothFactor = 0.9) {
- const previousAngleRad = this.ToRadians(previousAngle);
- const newAngleRad = this.ToRadians(newAngle);
- return this.ToDegrees(Math.atan2((1 - smoothFactor) * Math.sin(newAngleRad) + smoothFactor * Math.sin(previousAngleRad), (1 - smoothFactor) * Math.cos(newAngleRad) + smoothFactor * Math.cos(previousAngleRad)));
- }
- /**
- * Returns an array if obj is not an array
- * @param obj defines the object to evaluate as an array
- * @param allowsNullUndefined defines a boolean indicating if obj is allowed to be null or undefined
- * @returns either obj directly if obj is an array or a new array containing obj
- */
- static MakeArray(obj, allowsNullUndefined) {
- if (allowsNullUndefined !== true && (obj === undefined || obj == null)) {
- return null;
- }
- return Array.isArray(obj) ? obj : [obj];
- }
- /**
- * Gets the pointer prefix to use
- * @param engine defines the engine we are finding the prefix for
- * @returns "pointer" if touch is enabled. Else returns "mouse"
- */
- static GetPointerPrefix(engine) {
- let eventPrefix = "pointer";
- // Check if pointer events are supported
- if (IsWindowObjectExist() && !window.PointerEvent) {
- eventPrefix = "mouse";
- }
- // Special Fallback MacOS Safari...
- if (engine._badDesktopOS &&
- !engine._badOS &&
- // And not ipad pros who claim to be macs...
- !(document && "ontouchend" in document)) {
- eventPrefix = "mouse";
- }
- return eventPrefix;
- }
- /**
- * Sets the cors behavior on a dom element. This will add the required Tools.CorsBehavior to the element.
- * @param url define the url we are trying
- * @param element define the dom element where to configure the cors policy
- * @param element.crossOrigin
- */
- static SetCorsBehavior(url, element) {
- SetCorsBehavior(url, element);
- }
- /**
- * Sets the referrerPolicy behavior on a dom element.
- * @param referrerPolicy define the referrer policy to use
- * @param element define the dom element where to configure the referrer policy
- * @param element.referrerPolicy
- */
- static SetReferrerPolicyBehavior(referrerPolicy, element) {
- element.referrerPolicy = referrerPolicy;
- }
- // External files
- /**
- * Removes unwanted characters from an url
- * @param url defines the url to clean
- * @returns the cleaned url
- */
- static CleanUrl(url) {
- url = url.replace(/#/gm, "%23");
- return url;
- }
- /**
- * Gets or sets a function used to pre-process url before using them to load assets
- */
- static get PreprocessUrl() {
- return FileToolsOptions.PreprocessUrl;
- }
- static set PreprocessUrl(processor) {
- FileToolsOptions.PreprocessUrl = processor;
- }
- /**
- * Loads an image as an HTMLImageElement.
- * @param input url string, ArrayBuffer, or Blob to load
- * @param onLoad callback called when the image successfully loads
- * @param onError callback called when the image fails to load
- * @param offlineProvider offline provider for caching
- * @param mimeType optional mime type
- * @param imageBitmapOptions optional the options to use when creating an ImageBitmap
- * @returns the HTMLImageElement of the loaded image
- */
- static LoadImage(input, onLoad, onError, offlineProvider, mimeType, imageBitmapOptions) {
- return FileToolLoadImage(input, onLoad, onError, offlineProvider, mimeType, imageBitmapOptions);
- }
- /**
- * Loads a file from a url
- * @param url url string, ArrayBuffer, or Blob to load
- * @param onSuccess callback called when the file successfully loads
- * @param onProgress callback called while file is loading (if the server supports this mode)
- * @param offlineProvider defines the offline provider for caching
- * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
- * @param onError callback called when the file fails to load
- * @returns a file request object
- */
- static LoadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError) {
- return FileToolsLoadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError);
- }
- /**
- * Loads a file from a url
- * @param url the file url to load
- * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
- * @returns a promise containing an ArrayBuffer corresponding to the loaded file
- */
- static LoadFileAsync(url, useArrayBuffer = true) {
- return new Promise((resolve, reject) => {
- FileToolsLoadFile(url, (data) => {
- resolve(data);
- }, undefined, undefined, useArrayBuffer, (request, exception) => {
- reject(exception);
- });
- });
- }
- /**
- * Get a script URL including preprocessing
- * @param scriptUrl the script Url to process
- * @param forceAbsoluteUrl force the script to be an absolute url (adding the current base url if necessary)
- * @returns a modified URL to use
- */
- static GetBabylonScriptURL(scriptUrl, forceAbsoluteUrl) {
- if (!scriptUrl) {
- return "";
- }
- // if the base URL was set, and the script Url is an absolute path change the default path
- if (Tools.ScriptBaseUrl && scriptUrl.startsWith(Tools._DefaultCdnUrl)) {
- // change the default host, which is https://cdn.babylonjs.com with the one defined
- // make sure no trailing slash is present
- const baseUrl = Tools.ScriptBaseUrl[Tools.ScriptBaseUrl.length - 1] === "/" ? Tools.ScriptBaseUrl.substring(0, Tools.ScriptBaseUrl.length - 1) : Tools.ScriptBaseUrl;
- scriptUrl = scriptUrl.replace(Tools._DefaultCdnUrl, baseUrl);
- }
- // run the preprocessor
- scriptUrl = Tools.ScriptPreprocessUrl(scriptUrl);
- if (forceAbsoluteUrl) {
- scriptUrl = Tools.GetAbsoluteUrl(scriptUrl);
- }
- return scriptUrl;
- }
- /**
- * This function is used internally by babylon components to load a script (identified by an url). When the url returns, the
- * content of this file is added into a new script element, attached to the DOM (body element)
- * @param scriptUrl defines the url of the script to load
- * @param onSuccess defines the callback called when the script is loaded
- * @param onError defines the callback to call if an error occurs
- * @param scriptId defines the id of the script element
- */
- static LoadBabylonScript(scriptUrl, onSuccess, onError, scriptId) {
- scriptUrl = Tools.GetBabylonScriptURL(scriptUrl);
- Tools.LoadScript(scriptUrl, onSuccess, onError);
- }
- /**
- * Load an asynchronous script (identified by an url). When the url returns, the
- * content of this file is added into a new script element, attached to the DOM (body element)
- * @param scriptUrl defines the url of the script to laod
- * @returns a promise request object
- */
- static LoadBabylonScriptAsync(scriptUrl) {
- scriptUrl = Tools.GetBabylonScriptURL(scriptUrl);
- return Tools.LoadScriptAsync(scriptUrl);
- }
- /**
- * This function is used internally by babylon components to load a script (identified by an url). When the url returns, the
- * content of this file is added into a new script element, attached to the DOM (body element)
- * @param scriptUrl defines the url of the script to load
- * @param onSuccess defines the callback called when the script is loaded
- * @param onError defines the callback to call if an error occurs
- * @param scriptId defines the id of the script element
- */
- static LoadScript(scriptUrl, onSuccess, onError, scriptId) {
- if (typeof importScripts === "function") {
- try {
- importScripts(scriptUrl);
- onSuccess();
- }
- catch (e) {
- onError?.(`Unable to load script '${scriptUrl}' in worker`, e);
- }
- return;
- }
- else if (!IsWindowObjectExist()) {
- onError?.(`Cannot load script '${scriptUrl}' outside of a window or a worker`);
- return;
- }
- const head = document.getElementsByTagName("head")[0];
- const script = document.createElement("script");
- script.setAttribute("type", "text/javascript");
- script.setAttribute("src", scriptUrl);
- if (scriptId) {
- script.id = scriptId;
- }
- script.onload = () => {
- if (onSuccess) {
- onSuccess();
- }
- };
- script.onerror = (e) => {
- if (onError) {
- onError(`Unable to load script '${scriptUrl}'`, e);
- }
- };
- head.appendChild(script);
- }
- /**
- * Load an asynchronous script (identified by an url). When the url returns, the
- * content of this file is added into a new script element, attached to the DOM (body element)
- * @param scriptUrl defines the url of the script to load
- * @param scriptId defines the id of the script element
- * @returns a promise request object
- */
- static LoadScriptAsync(scriptUrl, scriptId) {
- return new Promise((resolve, reject) => {
- this.LoadScript(scriptUrl, () => {
- resolve();
- }, (message, exception) => {
- reject(exception || new Error(message));
- }, scriptId);
- });
- }
- /**
- * Loads a file from a blob
- * @param fileToLoad defines the blob to use
- * @param callback defines the callback to call when data is loaded
- * @param progressCallback defines the callback to call during loading process
- * @returns a file request object
- */
- static ReadFileAsDataURL(fileToLoad, callback, progressCallback) {
- const reader = new FileReader();
- const request = {
- onCompleteObservable: new Observable(),
- abort: () => reader.abort(),
- };
- reader.onloadend = () => {
- request.onCompleteObservable.notifyObservers(request);
- };
- reader.onload = (e) => {
- //target doesn't have result from ts 1.3
- callback(e.target["result"]);
- };
- reader.onprogress = progressCallback;
- reader.readAsDataURL(fileToLoad);
- return request;
- }
- /**
- * Reads a file from a File object
- * @param file defines the file to load
- * @param onSuccess defines the callback to call when data is loaded
- * @param onProgress defines the callback to call during loading process
- * @param useArrayBuffer defines a boolean indicating that data must be returned as an ArrayBuffer
- * @param onError defines the callback to call when an error occurs
- * @returns a file request object
- */
- static ReadFile(file, onSuccess, onProgress, useArrayBuffer, onError) {
- return FileToolsReadFile(file, onSuccess, onProgress, useArrayBuffer, onError);
- }
- /**
- * Creates a data url from a given string content
- * @param content defines the content to convert
- * @returns the new data url link
- */
- static FileAsURL(content) {
- const fileBlob = new Blob([content]);
- const url = window.URL;
- const link = url.createObjectURL(fileBlob);
- return link;
- }
- /**
- * Format the given number to a specific decimal format
- * @param value defines the number to format
- * @param decimals defines the number of decimals to use
- * @returns the formatted string
- */
- static Format(value, decimals = 2) {
- return value.toFixed(decimals);
- }
- /**
- * Tries to copy an object by duplicating every property
- * @param source defines the source object
- * @param destination defines the target object
- * @param doNotCopyList defines a list of properties to avoid
- * @param mustCopyList defines a list of properties to copy (even if they start with _)
- */
- static DeepCopy(source, destination, doNotCopyList, mustCopyList) {
- DeepCopier.DeepCopy(source, destination, doNotCopyList, mustCopyList);
- }
- /**
- * Gets a boolean indicating if the given object has no own property
- * @param obj defines the object to test
- * @returns true if object has no own property
- */
- static IsEmpty(obj) {
- for (const i in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, i)) {
- return false;
- }
- }
- return true;
- }
- /**
- * Function used to register events at window level
- * @param windowElement defines the Window object to use
- * @param events defines the events to register
- */
- static RegisterTopRootEvents(windowElement, events) {
- for (let index = 0; index < events.length; index++) {
- const event = events[index];
- windowElement.addEventListener(event.name, event.handler, false);
- try {
- if (window.parent) {
- window.parent.addEventListener(event.name, event.handler, false);
- }
- }
- catch (e) {
- // Silently fails...
- }
- }
- }
- /**
- * Function used to unregister events from window level
- * @param windowElement defines the Window object to use
- * @param events defines the events to unregister
- */
- static UnregisterTopRootEvents(windowElement, events) {
- for (let index = 0; index < events.length; index++) {
- const event = events[index];
- windowElement.removeEventListener(event.name, event.handler);
- try {
- if (windowElement.parent) {
- windowElement.parent.removeEventListener(event.name, event.handler);
- }
- }
- catch (e) {
- // Silently fails...
- }
- }
- }
- /**
- * Dumps the current bound framebuffer
- * @param width defines the rendering width
- * @param height defines the rendering height
- * @param engine defines the hosting engine
- * @param successCallback defines the callback triggered once the data are available
- * @param mimeType defines the mime type of the result
- * @param fileName defines the filename to download. If present, the result will automatically be downloaded
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- * @returns a void promise
- */
- static async DumpFramebuffer(width, height, engine, successCallback, mimeType = "image/png", fileName, quality) {
- throw _WarnImport("DumpTools");
- }
- /**
- * Dumps an array buffer
- * @param width defines the rendering width
- * @param height defines the rendering height
- * @param data the data array
- * @param successCallback defines the callback triggered once the data are available
- * @param mimeType defines the mime type of the result
- * @param fileName defines the filename to download. If present, the result will automatically be downloaded
- * @param invertY true to invert the picture in the Y dimension
- * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- */
- static DumpData(width, height, data, successCallback, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) {
- throw _WarnImport("DumpTools");
- }
- // eslint-disable-next-line jsdoc/require-returns-check
- /**
- * Dumps an array buffer
- * @param width defines the rendering width
- * @param height defines the rendering height
- * @param data the data array
- * @param mimeType defines the mime type of the result
- * @param fileName defines the filename to download. If present, the result will automatically be downloaded
- * @param invertY true to invert the picture in the Y dimension
- * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- * @returns a promise that resolve to the final data
- */
- static DumpDataAsync(width, height, data, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) {
- throw _WarnImport("DumpTools");
- }
- static _IsOffScreenCanvas(canvas) {
- return canvas.convertToBlob !== undefined;
- }
- /**
- * Converts the canvas data to blob.
- * This acts as a polyfill for browsers not supporting the to blob function.
- * @param canvas Defines the canvas to extract the data from (can be an offscreen canvas)
- * @param successCallback Defines the callback triggered once the data are available
- * @param mimeType Defines the mime type of the result
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- */
- static ToBlob(canvas, successCallback, mimeType = "image/png", quality) {
- // We need HTMLCanvasElement.toBlob for HD screenshots
- if (!Tools._IsOffScreenCanvas(canvas) && !canvas.toBlob) {
- // low performance polyfill based on toDataURL (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob)
- canvas.toBlob = function (callback, type, quality) {
- setTimeout(() => {
- const binStr = atob(this.toDataURL(type, quality).split(",")[1]), len = binStr.length, arr = new Uint8Array(len);
- for (let i = 0; i < len; i++) {
- arr[i] = binStr.charCodeAt(i);
- }
- callback(new Blob([arr]));
- });
- };
- }
- if (Tools._IsOffScreenCanvas(canvas)) {
- canvas
- .convertToBlob({
- type: mimeType,
- quality,
- })
- .then((blob) => successCallback(blob));
- }
- else {
- canvas.toBlob(function (blob) {
- successCallback(blob);
- }, mimeType, quality);
- }
- }
- /**
- * Download a Blob object
- * @param blob the Blob object
- * @param fileName the file name to download
- */
- static DownloadBlob(blob, fileName) {
- //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
- if ("download" in document.createElement("a")) {
- if (!fileName) {
- const date = new Date();
- const stringDate = (date.getFullYear() + "-" + (date.getMonth() + 1)).slice(2) + "-" + date.getDate() + "_" + date.getHours() + "-" + ("0" + date.getMinutes()).slice(-2);
- fileName = "screenshot_" + stringDate + ".png";
- }
- Tools.Download(blob, fileName);
- }
- else {
- if (blob && typeof URL !== "undefined") {
- const url = URL.createObjectURL(blob);
- const newWindow = window.open("");
- if (!newWindow) {
- return;
- }
- const img = newWindow.document.createElement("img");
- img.onload = function () {
- // no longer need to read the blob so it's revoked
- URL.revokeObjectURL(url);
- };
- img.src = url;
- newWindow.document.body.appendChild(img);
- }
- }
- }
- /**
- * Encodes the canvas data to base 64, or automatically downloads the result if `fileName` is defined.
- * @param canvas The canvas to get the data from, which can be an offscreen canvas.
- * @param successCallback The callback which is triggered once the data is available. If `fileName` is defined, the callback will be invoked after the download occurs, and the `data` argument will be an empty string.
- * @param mimeType The mime type of the result.
- * @param fileName The name of the file to download. If defined, the result will automatically be downloaded. If not defined, and `successCallback` is also not defined, the result will automatically be downloaded with an auto-generated file name.
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- */
- static EncodeScreenshotCanvasData(canvas, successCallback, mimeType = "image/png", fileName, quality) {
- if (typeof fileName === "string" || !successCallback) {
- this.ToBlob(canvas, function (blob) {
- if (blob) {
- Tools.DownloadBlob(blob, fileName);
- }
- if (successCallback) {
- successCallback("");
- }
- }, mimeType, quality);
- }
- else if (successCallback) {
- if (Tools._IsOffScreenCanvas(canvas)) {
- canvas
- .convertToBlob({
- type: mimeType,
- quality,
- })
- .then((blob) => {
- const reader = new FileReader();
- reader.readAsDataURL(blob);
- reader.onloadend = () => {
- const base64data = reader.result;
- successCallback(base64data);
- };
- });
- return;
- }
- const base64Image = canvas.toDataURL(mimeType, quality);
- successCallback(base64Image);
- }
- }
- /**
- * Downloads a blob in the browser
- * @param blob defines the blob to download
- * @param fileName defines the name of the downloaded file
- */
- static Download(blob, fileName) {
- if (typeof URL === "undefined") {
- return;
- }
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement("a");
- document.body.appendChild(a);
- a.style.display = "none";
- a.href = url;
- a.download = fileName;
- a.addEventListener("click", () => {
- if (a.parentElement) {
- a.parentElement.removeChild(a);
- }
- });
- a.click();
- window.URL.revokeObjectURL(url);
- }
- /**
- * Will return the right value of the noPreventDefault variable
- * Needed to keep backwards compatibility to the old API.
- *
- * @param args arguments passed to the attachControl function
- * @returns the correct value for noPreventDefault
- */
- static BackCompatCameraNoPreventDefault(args) {
- // is it used correctly?
- if (typeof args[0] === "boolean") {
- return args[0];
- }
- else if (typeof args[1] === "boolean") {
- return args[1];
- }
- return false;
- }
- /**
- * Captures a screenshot of the current rendering
- * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/renderToPNG
- * @param engine defines the rendering engine
- * @param camera defines the source camera
- * @param size This parameter can be set to a single number or to an object with the
- * following (optional) properties: precision, width, height. If a single number is passed,
- * it will be used for both width and height. If an object is passed, the screenshot size
- * will be derived from the parameters. The precision property is a multiplier allowing
- * rendering at a higher or lower resolution
- * @param successCallback defines the callback receives a single parameter which contains the
- * screenshot as a string of base64-encoded characters. This string can be assigned to the
- * src parameter of an <img> to display it
- * @param mimeType defines the MIME type of the screenshot image (default: image/png).
- * Check your browser for supported MIME types
- * @param forceDownload force the system to download the image even if a successCallback is provided
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- */
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static CreateScreenshot(engine, camera, size, successCallback, mimeType = "image/png", forceDownload = false, quality) {
- throw _WarnImport("ScreenshotTools");
- }
- // eslint-disable-next-line jsdoc/require-returns-check
- /**
- * Captures a screenshot of the current rendering
- * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/renderToPNG
- * @param engine defines the rendering engine
- * @param camera defines the source camera
- * @param size This parameter can be set to a single number or to an object with the
- * following (optional) properties: precision, width, height. If a single number is passed,
- * it will be used for both width and height. If an object is passed, the screenshot size
- * will be derived from the parameters. The precision property is a multiplier allowing
- * rendering at a higher or lower resolution
- * @param mimeType defines the MIME type of the screenshot image (default: image/png).
- * Check your browser for supported MIME types
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- * @returns screenshot as a string of base64-encoded characters. This string can be assigned
- * to the src parameter of an <img> to display it
- */
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static CreateScreenshotAsync(engine, camera, size, mimeType = "image/png", quality) {
- throw _WarnImport("ScreenshotTools");
- }
- /**
- * Generates an image screenshot from the specified camera.
- * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/renderToPNG
- * @param engine The engine to use for rendering
- * @param camera The camera to use for rendering
- * @param size This parameter can be set to a single number or to an object with the
- * following (optional) properties: precision, width, height. If a single number is passed,
- * it will be used for both width and height. If an object is passed, the screenshot size
- * will be derived from the parameters. The precision property is a multiplier allowing
- * rendering at a higher or lower resolution
- * @param successCallback The callback receives a single parameter which contains the
- * screenshot as a string of base64-encoded characters. This string can be assigned to the
- * src parameter of an <img> to display it
- * @param mimeType The MIME type of the screenshot image (default: image/png).
- * Check your browser for supported MIME types
- * @param samples Texture samples (default: 1)
- * @param antialiasing Whether antialiasing should be turned on or not (default: false)
- * @param fileName A name for for the downloaded file.
- * @param renderSprites Whether the sprites should be rendered or not (default: false)
- * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
- * @param useLayerMask if the camera's layer mask should be used to filter what should be rendered (default: true)
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- */
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static CreateScreenshotUsingRenderTarget(engine, camera, size, successCallback, mimeType = "image/png", samples = 1, antialiasing = false, fileName, renderSprites = false, enableStencilBuffer = false, useLayerMask = true, quality) {
- throw _WarnImport("ScreenshotTools");
- }
- // eslint-disable-next-line jsdoc/require-returns-check
- /**
- * Generates an image screenshot from the specified camera.
- * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/renderToPNG
- * @param engine The engine to use for rendering
- * @param camera The camera to use for rendering
- * @param size This parameter can be set to a single number or to an object with the
- * following (optional) properties: precision, width, height. If a single number is passed,
- * it will be used for both width and height. If an object is passed, the screenshot size
- * will be derived from the parameters. The precision property is a multiplier allowing
- * rendering at a higher or lower resolution
- * @param mimeType The MIME type of the screenshot image (default: image/png).
- * Check your browser for supported MIME types
- * @param samples Texture samples (default: 1)
- * @param antialiasing Whether antialiasing should be turned on or not (default: false)
- * @param fileName A name for for the downloaded file.
- * @returns screenshot as a string of base64-encoded characters. This string can be assigned
- * @param renderSprites Whether the sprites should be rendered or not (default: false)
- * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
- * @param useLayerMask if the camera's layer mask should be used to filter what should be rendered (default: true)
- * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
- * to the src parameter of an <img> to display it
- */
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static CreateScreenshotUsingRenderTargetAsync(engine, camera, size, mimeType = "image/png", samples = 1, antialiasing = false, fileName, renderSprites = false, enableStencilBuffer = false, useLayerMask = true, quality) {
- throw _WarnImport("ScreenshotTools");
- }
- /**
- * Implementation from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#answer-2117523
- * Be aware Math.random() could cause collisions, but:
- * "All but 6 of the 128 bits of the ID are randomly generated, which means that for any two ids, there's a 1 in 2^^122 (or 5.3x10^^36) chance they'll collide"
- * @returns a pseudo random id
- */
- static RandomId() {
- return RandomGUID();
- }
- /**
- * Test if the given uri is a base64 string
- * @deprecated Please use FileTools.IsBase64DataUrl instead.
- * @param uri The uri to test
- * @returns True if the uri is a base64 string or false otherwise
- */
- static IsBase64(uri) {
- return IsBase64DataUrl(uri);
- }
- /**
- * Decode the given base64 uri.
- * @deprecated Please use FileTools.DecodeBase64UrlToBinary instead.
- * @param uri The uri to decode
- * @returns The decoded base64 data.
- */
- static DecodeBase64(uri) {
- return DecodeBase64UrlToBinary(uri);
- }
- /**
- * Gets a value indicating the number of loading errors
- * @ignorenaming
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static get errorsCount() {
- return Logger.errorsCount;
- }
- /**
- * Log a message to the console
- * @param message defines the message to log
- */
- static Log(message) {
- Logger.Log(message);
- }
- /**
- * Write a warning message to the console
- * @param message defines the message to log
- */
- static Warn(message) {
- Logger.Warn(message);
- }
- /**
- * Write an error message to the console
- * @param message defines the message to log
- */
- static Error(message) {
- Logger.Error(message);
- }
- /**
- * Gets current log cache (list of logs)
- */
- static get LogCache() {
- return Logger.LogCache;
- }
- /**
- * Clears the log cache
- */
- static ClearLogCache() {
- Logger.ClearLogCache();
- }
- /**
- * Sets the current log level (MessageLogLevel / WarningLogLevel / ErrorLogLevel)
- */
- static set LogLevels(level) {
- Logger.LogLevels = level;
- }
- /**
- * Sets the current performance log level
- */
- static set PerformanceLogLevel(level) {
- if ((level & Tools.PerformanceUserMarkLogLevel) === Tools.PerformanceUserMarkLogLevel) {
- Tools.StartPerformanceCounter = Tools._StartUserMark;
- Tools.EndPerformanceCounter = Tools._EndUserMark;
- return;
- }
- if ((level & Tools.PerformanceConsoleLogLevel) === Tools.PerformanceConsoleLogLevel) {
- Tools.StartPerformanceCounter = Tools._StartPerformanceConsole;
- Tools.EndPerformanceCounter = Tools._EndPerformanceConsole;
- return;
- }
- Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
- Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
- }
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static _StartPerformanceCounterDisabled(counterName, condition) { }
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- static _EndPerformanceCounterDisabled(counterName, condition) { }
- static _StartUserMark(counterName, condition = true) {
- if (!Tools._Performance) {
- if (!IsWindowObjectExist()) {
- return;
- }
- Tools._Performance = window.performance;
- }
- if (!condition || !Tools._Performance.mark) {
- return;
- }
- Tools._Performance.mark(counterName + "-Begin");
- }
- static _EndUserMark(counterName, condition = true) {
- if (!condition || !Tools._Performance.mark) {
- return;
- }
- Tools._Performance.mark(counterName + "-End");
- Tools._Performance.measure(counterName, counterName + "-Begin", counterName + "-End");
- }
- static _StartPerformanceConsole(counterName, condition = true) {
- if (!condition) {
- return;
- }
- Tools._StartUserMark(counterName, condition);
- if (console.time) {
- console.time(counterName);
- }
- }
- static _EndPerformanceConsole(counterName, condition = true) {
- if (!condition) {
- return;
- }
- Tools._EndUserMark(counterName, condition);
- console.timeEnd(counterName);
- }
- /**
- * Gets either window.performance.now() if supported or Date.now() else
- */
- static get Now() {
- return PrecisionDate.Now;
- }
- /**
- * This method will return the name of the class used to create the instance of the given object.
- * It will works only on Javascript basic data types (number, string, ...) and instance of class declared with the @className decorator.
- * @param object the object to get the class name from
- * @param isType defines if the object is actually a type
- * @returns the name of the class, will be "object" for a custom data type not using the @className decorator
- */
- static GetClassName(object, isType = false) {
- let name = null;
- if (!isType && object.getClassName) {
- name = object.getClassName();
- }
- else {
- if (object instanceof Object) {
- const classObj = isType ? object : Object.getPrototypeOf(object);
- name = classObj.constructor["__bjsclassName__"];
- }
- if (!name) {
- name = typeof object;
- }
- }
- return name;
- }
- /**
- * Gets the first element of an array satisfying a given predicate
- * @param array defines the array to browse
- * @param predicate defines the predicate to use
- * @returns null if not found or the element
- */
- static First(array, predicate) {
- for (const el of array) {
- if (predicate(el)) {
- return el;
- }
- }
- return null;
- }
- /**
- * This method will return the name of the full name of the class, including its owning module (if any).
- * It will works only on Javascript basic data types (number, string, ...) and instance of class declared with the @className decorator or implementing a method getClassName():string (in which case the module won't be specified).
- * @param object the object to get the class name from
- * @param isType defines if the object is actually a type
- * @returns a string that can have two forms: "moduleName.className" if module was specified when the class' Name was registered or "className" if there was not module specified.
- * @ignorenaming
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static getFullClassName(object, isType = false) {
- let className = null;
- let moduleName = null;
- if (!isType && object.getClassName) {
- className = object.getClassName();
- }
- else {
- if (object instanceof Object) {
- const classObj = isType ? object : Object.getPrototypeOf(object);
- className = classObj.constructor["__bjsclassName__"];
- moduleName = classObj.constructor["__bjsmoduleName__"];
- }
- if (!className) {
- className = typeof object;
- }
- }
- if (!className) {
- return null;
- }
- return (moduleName != null ? moduleName + "." : "") + className;
- }
- /**
- * Returns a promise that resolves after the given amount of time.
- * @param delay Number of milliseconds to delay
- * @returns Promise that resolves after the given amount of time
- */
- static DelayAsync(delay) {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve();
- }, delay);
- });
- }
- /**
- * Utility function to detect if the current user agent is Safari
- * @returns whether or not the current user agent is safari
- */
- static IsSafari() {
- if (!IsNavigatorAvailable()) {
- return false;
- }
- return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
- }
- }
- /**
- * Enable/Disable Custom HTTP Request Headers globally.
- * default = false
- * @see CustomRequestHeaders
- */
- Tools.UseCustomRequestHeaders = false;
- /**
- * Custom HTTP Request Headers to be sent with XMLHttpRequests
- * i.e. when loading files, where the server/service expects an Authorization header
- */
- Tools.CustomRequestHeaders = WebRequest.CustomRequestHeaders;
- /**
- * Extracts text content from a DOM element hierarchy
- * Back Compat only, please use GetDOMTextContent instead.
- */
- Tools.GetDOMTextContent = GetDOMTextContent;
- /**
- * @internal
- */
- Tools._DefaultCdnUrl = "https://cdn.babylonjs.com";
- // eslint-disable-next-line jsdoc/require-returns-check, jsdoc/require-param
- /**
- * @returns the absolute URL of a given (relative) url
- */
- Tools.GetAbsoluteUrl = typeof document === "object"
- ? (url) => {
- const a = document.createElement("a");
- a.href = url;
- return a.href;
- }
- : typeof URL === "function" && typeof location === "object"
- ? (url) => new URL(url, location.origin).href
- : () => {
- throw new Error("Unable to get absolute URL. Override BABYLON.Tools.GetAbsoluteUrl to a custom implementation for the current context.");
- };
- // Logs
- /**
- * No log
- */
- Tools.NoneLogLevel = Logger.NoneLogLevel;
- /**
- * Only message logs
- */
- Tools.MessageLogLevel = Logger.MessageLogLevel;
- /**
- * Only warning logs
- */
- Tools.WarningLogLevel = Logger.WarningLogLevel;
- /**
- * Only error logs
- */
- Tools.ErrorLogLevel = Logger.ErrorLogLevel;
- /**
- * All logs
- */
- Tools.AllLogLevel = Logger.AllLogLevel;
- /**
- * Checks if the window object exists
- * Back Compat only, please use IsWindowObjectExist instead.
- */
- Tools.IsWindowObjectExist = IsWindowObjectExist;
- // Performances
- /**
- * No performance log
- */
- Tools.PerformanceNoneLogLevel = 0;
- /**
- * Use user marks to log performance
- */
- Tools.PerformanceUserMarkLogLevel = 1;
- /**
- * Log performance to the console
- */
- Tools.PerformanceConsoleLogLevel = 2;
- /**
- * Starts a performance counter
- */
- Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
- /**
- * Ends a specific performance counter
- */
- Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
- /**
- * Use this className as a decorator on a given class definition to add it a name and optionally its module.
- * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
- * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified
- * @param name The name of the class, case should be preserved
- * @param module The name of the Module hosting the class, optional, but strongly recommended to specify if possible. Case should be preserved.
- * @returns a decorator function to apply on the class definition.
- */
- export function className(name, module) {
- return (target) => {
- target["__bjsclassName__"] = name;
- target["__bjsmoduleName__"] = module != null ? module : null;
- };
- }
- /**
- * An implementation of a loop for asynchronous functions.
- */
- export class AsyncLoop {
- /**
- * Constructor.
- * @param iterations the number of iterations.
- * @param func the function to run each iteration
- * @param successCallback the callback that will be called upon successful execution
- * @param offset starting offset.
- */
- constructor(
- /**
- * Defines the number of iterations for the loop
- */
- iterations, func, successCallback, offset = 0) {
- this.iterations = iterations;
- this.index = offset - 1;
- this._done = false;
- this._fn = func;
- this._successCallback = successCallback;
- }
- /**
- * Execute the next iteration. Must be called after the last iteration was finished.
- */
- executeNext() {
- if (!this._done) {
- if (this.index + 1 < this.iterations) {
- ++this.index;
- this._fn(this);
- }
- else {
- this.breakLoop();
- }
- }
- }
- /**
- * Break the loop and run the success callback.
- */
- breakLoop() {
- this._done = true;
- this._successCallback();
- }
- /**
- * Create and run an async loop.
- * @param iterations the number of iterations.
- * @param fn the function to run each iteration
- * @param successCallback the callback that will be called upon successful execution
- * @param offset starting offset.
- * @returns the created async loop object
- */
- static Run(iterations, fn, successCallback, offset = 0) {
- const loop = new AsyncLoop(iterations, fn, successCallback, offset);
- loop.executeNext();
- return loop;
- }
- /**
- * A for-loop that will run a given number of iterations synchronous and the rest async.
- * @param iterations total number of iterations
- * @param syncedIterations number of synchronous iterations in each async iteration.
- * @param fn the function to call each iteration.
- * @param callback a success call back that will be called when iterating stops.
- * @param breakFunction a break condition (optional)
- * @param timeout timeout settings for the setTimeout function. default - 0.
- * @returns the created async loop object
- */
- static SyncAsyncForLoop(iterations, syncedIterations, fn, callback, breakFunction, timeout = 0) {
- return AsyncLoop.Run(Math.ceil(iterations / syncedIterations), (loop) => {
- if (breakFunction && breakFunction()) {
- loop.breakLoop();
- }
- else {
- setTimeout(() => {
- for (let i = 0; i < syncedIterations; ++i) {
- const iteration = loop.index * syncedIterations + i;
- if (iteration >= iterations) {
- break;
- }
- fn(iteration);
- if (breakFunction && breakFunction()) {
- loop.breakLoop();
- break;
- }
- }
- loop.executeNext();
- }, timeout);
- }
- }, callback);
- }
- }
- Tools.Mix = Mix;
- Tools.IsExponentOfTwo = IsExponentOfTwo;
- // Will only be define if Tools is imported freeing up some space when only engine is required
- EngineStore.FallbackTexture =
- "data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAAABgAAAAAQAAAGAAAAABcGFpbnQubmV0IDQuMC41AP/bAEMABAIDAwMCBAMDAwQEBAQFCQYFBQUFCwgIBgkNCw0NDQsMDA4QFBEODxMPDAwSGBITFRYXFxcOERkbGRYaFBYXFv/bAEMBBAQEBQUFCgYGChYPDA8WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFv/AABEIAQABAAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APH6KKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76CiiigD5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BQooooA+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/voKKKKAPl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FCiiigD6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++gooooA+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gUKKKKAPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76Pl+iiivuj+BT6gooor4U/vo+X6KKK+6P4FPqCiiivhT++j5fooor7o/gU+oKKKK+FP76P//Z";
- //# sourceMappingURL=tools.js.map
|