<!-- NEW4 — TEST 4 · THE QUILT · part 18 -->
etConsoleFunction( 'warn', message, ...params );

	} else {

		const stackTrace = params[ 0 ];

		if ( stackTrace && stackTrace.isStackTrace ) {

			console.warn( stackTrace.getError( message ) );

		} else {

			console.warn( message, ...params );

		}

	}

}

/**
 * Logs an error message with the 'THREE.' prefix.
 *
 * If a custom console function is set via setConsoleFunction(), it will be used
 * instead of the native console.error. The first parameter is treated as the
 * method name and is automatically prefixed with 'THREE.'.
 *
 * @param {...any} params - The message components. The first param is used as
 *                          the method name and prefixed with 'THREE.'.
 */
function error( ...params ) {

	params = enhanceLogMessage( params );

	const message = 'THREE.' + params.shift();

	if ( _setConsoleFunction ) {

		_setConsoleFunction( 'error', message, ...params );

	} else {

		const stackTrace = params[ 0 ];

		if ( stackTrace && stackTrace.isStackTrace ) {

			console.error( stackTrace.getError( message ) );

		} else {

			console.error( message, ...params );

		}

	}

}

/**
 * Logs a warning message only once, preventing duplicate warnings.
 *
 * This function maintains an internal cache of warning messages and will only
 * output each unique warning message once. Useful for warnings that may be
 * triggered repeatedly but should only be shown to the user once.
 *
 * @param {...any} params - The warning message components.
 */
function warnOnce( ...params ) {

	const message = params.join( ' ' );

	if ( message in _cache ) return;

	_cache[ message ] = true;

	warn( ...params );

}

/**
 * Yields execution to the main thread to allow rendering and other tasks.
 * Uses scheduler.yield() when available (Chrome 115+), falls back to requestAnimationFrame.
 *
 * @return {Promise<void>}
 */
function yieldToMain() {

	if ( typeof self !== 'undefined' && typeof self.scheduler !== 'undefined' && typeof self.scheduler.yield !== 'undefined' ) {

		return self.scheduler.yield();

	}

	return new Promise( resolve => {

		requestAnimationFrame( resolve );

	} );

}

/**
 * Asynchronously probes for WebGL sync object completion.
 *
 * This function creates a promise that resolves when the WebGL sync object
 * signals completion or rejects if the sync operation fails. It uses polling
 * at the specified interval to check the sync status without blocking the
 * main thread. This is useful for GPU-CPU synchronization in WebGL contexts.
 *
 * @private
 * @param {WebGL2RenderingContext} gl - The WebGL rendering context.
 * @param {WebGLSync} sync - The WebGL sync object to wait for.
 * @param {number} interval - The polling interval in milliseconds.
 * @return {Promise<void>} A promise that resolves when the sync completes or rejects if it fails.
 */
function probeAsync( gl, sync, interval ) {

	return new Promise( function ( resolve, reject ) {

		function probe() {

			switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) {

				case gl.WAIT_FAILED:
					reject();
					break;

				case gl.TIMEOUT_EXPIRED:
					setTimeout( probe, interval );
					break;

				default:
					resolve();

			}

		}

		setTimeout( probe, interval );

	} );

}

/**
 * Used to select the correct depth functions
 * when reversed depth buffer is used.
 *
 * @private
 * @type {Object}
 */
const ReversedDepthFuncs = {
	[ NeverDepth ]: AlwaysDepth,
	[ LessDepth ]: GreaterDepth,
	[ EqualDepth ]: NotEqualDepth,
	[ LessEqualDepth ]: GreaterEqualDepth,

	[ AlwaysDepth ]: NeverDepth,
	[ GreaterDepth ]: LessDepth,
	[ NotEqualDepth ]: EqualDepth,
	[ GreaterEqualDepth ]: LessEqualDepth,
};

/**
 * This modules allows to dispatch event objects on custom JavaScript objects.
 *
 * Main repository: [eventdispatcher.js](https://github.com/mrdoob/eventdispatcher.js/)
 *
 * Code Example:
 * ```js
 * class Car extends EventDispatcher {
 * 	start() {
 *		this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
 *	}
 *};
 *
 * // Using events with the custom object
 * const car = new Car();
 * car.addEventListener( 'start', function ( event ) {
 * 	alert( event.message );
 * } );
 *
 * car.start();
 * ```
 */
class EventDispatcher {

	/**
	 * Adds the given event listener to the given event type.
	 *
	 * @param {string} type - The type of event to listen to.
	 * @param {Function} listener - The function that gets called when the event is fired.
	 */
	addEventListener( type, listener ) {

		if ( this._listeners === undefined ) this._listeners = {};

		const listeners = this._listeners;

		if ( listeners[ type ] === undefined ) {

			listeners[ type ] = [];

		}

		if ( listeners[ type ].indexOf( listener ) === -1 ) {

			listeners[ type ].push( listener );

		}

	}

	/**
	 * Returns `true` if the given event listener has been added to the given event type.
	 *
	 * @param {string} type - The type of event.
	 * @param {Function} listener - The listener to check.
	 * @return {boole