/* X_ITE v15.1.8 */
var __webpack_modules__ = ({

/***/ 894
(module, exports) {

var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
	SuperGif

	Example usage:

		<img src="./example1_preview.gif" rel:animated_src="./example1.gif" width="360" height="360" rel:auto_play="1" />

		<script type="text/javascript">
			$$('img').each(function (img_tag) {
				if (/.*\.gif/.test(img_tag.src)) {
					var rub = new SuperGif({ gif: img_tag } );
					rub.load();
				}
			});
		</script>

	Image tag attributes:

		rel:animated_src -	If this url is specified, it's loaded into the player instead of src.
							This allows a preview frame to be shown until animated gif data is streamed into the canvas

		rel:auto_play -		Defaults to 1 if not specified. If set to zero, a call to the play() method is needed

	Constructor options args

		gif 				Required. The DOM element of an img tag.
		loop_mode			Optional. Setting this to false will force disable looping of the gif.
		auto_play 			Optional. Same as the rel:auto_play attribute above, this arg overrides the img tag info.
		max_width			Optional. Scale images over max_width down to max_width. Helpful with mobile.
 		on_end				Optional. Add a callback for when the gif reaches the end of a single loop (one iteration). The first argument passed will be the gif HTMLElement.
		loop_delay			Optional. The amount of time to pause (in ms) after each single loop (iteration).
		draw_while_loading	Optional. Determines whether the gif will be drawn to the canvas whilst it is loaded.
		show_progress_bar	Optional. Only applies when draw_while_loading is set to true.
        on_error            Optional. Add a callback for when a load error occurs.

	Instance methods

		// loading
		load( callback )		Loads the gif specified by the src or rel:animated_src sttributie of the img tag into a canvas element and then calls callback if one is passed
		load_url( src, callback )	Loads the gif file specified in the src argument into a canvas element and then calls callback if one is passed

		// play controls
		play -				Start playing the gif
		pause -				Stop playing the gif
		move_to(i) -		Move to frame i of the gif
		move_relative(i) -	Move i frames ahead (or behind if i < 0)

		// getters
		get_canvas			The canvas element that the gif is playing in. Handy for assigning event handlers to.
		get_playing			Whether or not the gif is currently playing
		get_loading			Whether or not the gif has finished loading/parsing
		get_auto_play		Whether or not the gif is set to play automatically
		get_length			The number of frames in the gif
		get_current_frame	The index of the currently displayed frame of the gif
		get_frames	        An array containing the data for all parsed frames
		get_duration	    Returns the duration of the gif in hundredths of a second (standard for GIF spec)
		get_duration_ms	    Returns the duration of the gif in milliseconds

		For additional customization (viewport inside iframe) these params may be passed:
		c_w, c_h - width and height of canvas
		vp_t, vp_l, vp_ w, vp_h - top, left, width and height of the viewport

		A bonus: few articles to understand what is going on
			http://enthusiasms.org/post/16976438906
			http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
			http://humpy77.deviantart.com/journal/Frame-Delay-Times-for-Animated-GIFs-214150546

*/
(function (root, factory) {
    if (true) {
        !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
		__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
		(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
		__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    } else // removed by dead control flow
{}
}(this, function () {
    // Generic functions
    var bitsToNum = function (ba) {
        return ba.reduce(function (s, n) {
            return s * 2 + n;
        }, 0);
    };

    var byteToBitArr = function (bite) {
        var a = [];
        for (var i = 7; i >= 0; i--) {
            a.push( !! (bite & (1 << i)));
        }
        return a;
    };

    // Stream
    /**
     * @constructor
     */
    // Make compiler happy.
    var Stream = function (data) {
        this.data = data;
        this.len = this.data.length;
        this.pos = 0;

        this.readByte = function () {
            if (this.pos >= this.data.length) {
                throw new Error('Attempted to read past end of stream.');
            }
            if (data instanceof Uint8Array)
                return data[this.pos++];
            else
                return data.charCodeAt(this.pos++) & 0xFF;
        };

        this.readBytes = function (n) {
            var bytes = [];
            for (var i = 0; i < n; i++) {
                bytes.push(this.readByte());
            }
            return bytes;
        };

        this.read = function (n) {
            var s = '';
            for (var i = 0; i < n; i++) {
                s += String.fromCharCode(this.readByte());
            }
            return s;
        };

        this.readUnsigned = function () { // Little-endian.
            var a = this.readBytes(2);
            return (a[1] << 8) + a[0];
        };
    };

    var lzwDecode = function (minCodeSize, data) {
        // TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
        var pos = 0; // Maybe this streaming thing should be merged with the Stream?
        var readCode = function (size) {
            var code = 0;
            for (var i = 0; i < size; i++) {
                if (data.charCodeAt(pos >> 3) & (1 << (pos & 7))) {
                    code |= 1 << i;
                }
                pos++;
            }
            return code;
        };

        var output = [];

        var clearCode = 1 << minCodeSize;
        var eoiCode = clearCode + 1;

        var codeSize = minCodeSize + 1;

        var dict = [];

        var clear = function () {
            dict = [];
            codeSize = minCodeSize + 1;
            for (var i = 0; i < clearCode; i++) {
                dict[i] = [i];
            }
            dict[clearCode] = [];
            dict[eoiCode] = null;

        };

        var code;
        var last;

        while (true) {
            last = code;
            code = readCode(codeSize);

            if (code === clearCode) {
                clear();
                continue;
            }
            if (code === eoiCode) break;

            if (code < dict.length) {
                if (last !== clearCode) {
                    dict.push(dict[last].concat(dict[code][0]));
                }
            }
            else {
                if (code !== dict.length) throw new Error('Invalid LZW code.');
                dict.push(dict[last].concat(dict[last][0]));
            }
            output.push.apply(output, dict[code]);

            if (dict.length === (1 << codeSize) && codeSize < 12) {
                // If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
                codeSize++;
            }
        }

        // I don't know if this is technically an error, but some GIFs do it.
        //if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
        return output;
    };


    // The actual parsing; returns an object with properties.
    var parseGIF = function (st, handler) {
        handler || (handler = {});

        // LZW (GIF-specific)
        var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
            var ct = [];
            for (var i = 0; i < entries; i++) {
                ct.push(st.readBytes(3));
            }
            return ct;
        };

        var readSubBlocks = function () {
            var size, data;
            data = '';
            do {
                size = st.readByte();
                data += st.read(size);
            } while (size !== 0);
            return data;
        };

        var parseHeader = function () {
            var hdr = {};
            hdr.sig = st.read(3);
            hdr.ver = st.read(3);
            if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely.
            hdr.width = st.readUnsigned();
            hdr.height = st.readUnsigned();

            var bits = byteToBitArr(st.readByte());
            hdr.gctFlag = bits.shift();
            hdr.colorRes = bitsToNum(bits.splice(0, 3));
            hdr.sorted = bits.shift();
            hdr.gctSize = bitsToNum(bits.splice(0, 3));

            hdr.bgColor = st.readByte();
            hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
            if (hdr.gctFlag) {
                hdr.gct = parseCT(1 << (hdr.gctSize + 1));
            }
            handler.hdr && handler.hdr(hdr);
        };

        var parseExt = function (block) {
            var parseGCExt = function (block) {
                var blockSize = st.readByte(); // Always 4
                var bits = byteToBitArr(st.readByte());
                block.reserved = bits.splice(0, 3); // Reserved; should be 000.
                block.disposalMethod = bitsToNum(bits.splice(0, 3));
                block.userInput = bits.shift();
                block.transparencyGiven = bits.shift();

                block.delayTime = st.readUnsigned();

                block.transparencyIndex = st.readByte();

                block.terminator = st.readByte();

                handler.gce && handler.gce(block);
            };

            var parseComExt = function (block) {
                block.comment = readSubBlocks();
                handler.com && handler.com(block);
            };

            var parsePTExt = function (block) {
                // No one *ever* uses this. If you use it, deal with parsing it yourself.
                var blockSize = st.readByte(); // Always 12
                block.ptHeader = st.readBytes(12);
                block.ptData = readSubBlocks();
                handler.pte && handler.pte(block);
            };

            var parseAppExt = function (block) {
                var parseNetscapeExt = function (block) {
                    var blockSize = st.readByte(); // Always 3
                    block.unknown = st.readByte(); // ??? Always 1? What is this?
                    block.iterations = st.readUnsigned();
                    block.terminator = st.readByte();
                    handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block);
                };

                var parseUnknownAppExt = function (block) {
                    block.appData = readSubBlocks();
                    // FIXME: This won't work if a handler wants to match on any identifier.
                    handler.app && handler.app[block.identifier] && handler.app[block.identifier](block);
                };

                var blockSize = st.readByte(); // Always 11
                block.identifier = st.read(8);
                block.authCode = st.read(3);
                switch (block.identifier) {
                    case 'NETSCAPE':
                        parseNetscapeExt(block);
                        break;
                    default:
                        parseUnknownAppExt(block);
                        break;
                }
            };

            var parseUnknownExt = function (block) {
                block.data = readSubBlocks();
                handler.unknown && handler.unknown(block);
            };

            block.label = st.readByte();
            switch (block.label) {
                case 0xF9:
                    block.extType = 'gce';
                    parseGCExt(block);
                    break;
                case 0xFE:
                    block.extType = 'com';
                    parseComExt(block);
                    break;
                case 0x01:
                    block.extType = 'pte';
                    parsePTExt(block);
                    break;
                case 0xFF:
                    block.extType = 'app';
                    parseAppExt(block);
                    break;
                default:
                    block.extType = 'unknown';
                    parseUnknownExt(block);
                    break;
            }
        };

        var parseImg = function (img) {
            var deinterlace = function (pixels, width) {
                // Of course this defeats the purpose of interlacing. And it's *probably*
                // the least efficient way it's ever been implemented. But nevertheless...
                var newPixels = new Array(pixels.length);
                var rows = pixels.length / width;
                var cpRow = function (toRow, fromRow) {
                    var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);
                    newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));
                };

                // See appendix E.
                var offsets = [0, 4, 2, 1];
                var steps = [8, 8, 4, 2];

                var fromRow = 0;
                for (var pass = 0; pass < 4; pass++) {
                    for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
                        cpRow(toRow, fromRow)
                        fromRow++;
                    }
                }

                return newPixels;
            };

            img.leftPos = st.readUnsigned();
            img.topPos = st.readUnsigned();
            img.width = st.readUnsigned();
            img.height = st.readUnsigned();

            var bits = byteToBitArr(st.readByte());
            img.lctFlag = bits.shift();
            img.interlaced = bits.shift();
            img.sorted = bits.shift();
            img.reserved = bits.splice(0, 2);
            img.lctSize = bitsToNum(bits.splice(0, 3));

            if (img.lctFlag) {
                img.lct = parseCT(1 << (img.lctSize + 1));
            }

            img.lzwMinCodeSize = st.readByte();

            var lzwData = readSubBlocks();

            img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData);

            if (img.interlaced) { // Move
                img.pixels = deinterlace(img.pixels, img.width);
            }

            handler.img && handler.img(img);
        };

        var parseBlock = function () {
            var block = {};
            block.sentinel = st.readByte();

            switch (String.fromCharCode(block.sentinel)) { // For ease of matching
                case '!':
                    block.type = 'ext';
                    parseExt(block);
                    break;
                case ',':
                    block.type = 'img';
                    parseImg(block);
                    break;
                case ';':
                    block.type = 'eof';
                    handler.eof && handler.eof(block);
                    break;
                default:
                    throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0.
            }

            if (block.type !== 'eof') setTimeout(parseBlock, 0);
        };

        var parse = function () {
            parseHeader();
            setTimeout(parseBlock, 0);
        };

        parse();
    };

    var SuperGif = function ( opts ) {
        var options = {
            //viewport position
            vp_l: 0,
            vp_t: 0,
            vp_w: null,
            vp_h: null,
            //canvas sizes
            c_w: null,
            c_h: null
        };
        for (var i in opts ) { options[i] = opts[i] }
        if (options.vp_w && options.vp_h) options.is_vp = true;

        var stream;
        var hdr;

        var loadError = null;
        var loading = false;

        var transparency = null;
        var delay = null;
        var disposalMethod = null;
        var disposalRestoreFromIdx = null;
        var lastDisposalMethod = null;
        var frame = null;
        var lastImg = null;

        var playing = true;
        var forward = true;

        var ctx_scaled = false;

        var frames = [];
        var frameOffsets = []; // elements have .x and .y properties

        var gif = options.gif;
        if (typeof options.auto_play == 'undefined')
            options.auto_play = (!gif.getAttribute('rel:auto_play') || gif.getAttribute('rel:auto_play') == '1');

        var onEndListener = (options.hasOwnProperty('on_end') ? options.on_end : null);
        var onErrorListener = (options.hasOwnProperty('on_error') ? options.on_error : null);
        var loopDelay = (options.hasOwnProperty('loop_delay') ? options.loop_delay : 0);
        var overrideLoopMode = (options.hasOwnProperty('loop_mode') ? options.loop_mode : 'auto');
        var drawWhileLoading = (options.hasOwnProperty('draw_while_loading') ? options.draw_while_loading : true);
        var showProgressBar = drawWhileLoading ? (options.hasOwnProperty('show_progress_bar') ? options.show_progress_bar : true) : false;
        var progressBarHeight = (options.hasOwnProperty('progressbar_height') ? options.progressbar_height : 25);
        var progressBarBackgroundColor = (options.hasOwnProperty('progressbar_background_color') ? options.progressbar_background_color : 'rgba(255,255,255,0.4)');
        var progressBarForegroundColor = (options.hasOwnProperty('progressbar_foreground_color') ? options.progressbar_foreground_color : 'rgba(255,0,22,.8)');

        var clear = function () {
            transparency = null;
            delay = null;
            lastDisposalMethod = disposalMethod;
            disposalMethod = null;
            frame = null;
        };

        // XXX: There's probably a better way to handle catching exceptions when
        // callbacks are involved.
        var doParse = function () {
            try {
                parseGIF(stream, handler);
            }
            catch (err) {
                doLoadError('parse');
            }
        };

        var doText = function (text) {
            toolbar.innerHTML = text; // innerText? Escaping? Whatever.
            toolbar.style.visibility = 'visible';
        };

        var setSizes = function(w, h) {
            canvas.width = w * get_canvas_scale();
            canvas.height = h * get_canvas_scale();
            toolbar.style.minWidth = ( w * get_canvas_scale() ) + 'px';

            tmpCanvas.width = w;
            tmpCanvas.height = h;
            tmpCanvas.style.width = w + 'px';
            tmpCanvas.style.height = h + 'px';
            tmpCanvas.getContext('2d', { willReadFrequently: true }).setTransform(1, 0, 0, 1, 0, 0);
        };

        var setFrameOffset = function(frame, offset) {
            if (!frameOffsets[frame]) {
                frameOffsets[frame] = offset;
                return;
            }
            if (typeof offset.x !== 'undefined') {
                frameOffsets[frame].x = offset.x;
            }
            if (typeof offset.y !== 'undefined') {
                frameOffsets[frame].y = offset.y;
            }
        };

        var doShowProgress = function (pos, length, draw) {
            if (draw && showProgressBar) {
                var height = progressBarHeight;
                var left, mid, top, width;
                if (options.is_vp) {
                    if (!ctx_scaled) {
                        top = (options.vp_t + options.vp_h - height);
                        height = height;
                        left = options.vp_l;
                        mid = left + (pos / length) * options.vp_w;
                        width = canvas.width;
                    } else {
                        top = (options.vp_t + options.vp_h - height) / get_canvas_scale();
                        height = height / get_canvas_scale();
                        left = (options.vp_l / get_canvas_scale() );
                        mid = left + (pos / length) * (options.vp_w / get_canvas_scale());
                        width = canvas.width / get_canvas_scale();
                    }
                    //some debugging, draw rect around viewport
                    if (false) // removed by dead control flow
{ var w, h, l, t; }
                }
                else {
                    top = (canvas.height - height) / (ctx_scaled ? get_canvas_scale() : 1);
                    mid = ((pos / length) * canvas.width) / (ctx_scaled ? get_canvas_scale() : 1);
                    width = canvas.width / (ctx_scaled ? get_canvas_scale() : 1 );
                    height /= ctx_scaled ? get_canvas_scale() : 1;
                }

                ctx.fillStyle = progressBarBackgroundColor;
                ctx.fillRect(mid, top, width - mid, height);

                ctx.fillStyle = progressBarForegroundColor;
                ctx.fillRect(0, top, mid, height);
            }
        };

        var doLoadError = function (originOfError) {
            var drawError = function () {
                ctx.fillStyle = 'black';
                ctx.fillRect(0, 0, options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
                ctx.strokeStyle = 'red';
                ctx.lineWidth = 3;
                ctx.moveTo(0, 0);
                ctx.lineTo(options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
                ctx.moveTo(0, options.c_h ? options.c_h : hdr.height);
                ctx.lineTo(options.c_w ? options.c_w : hdr.width, 0);
                ctx.stroke();
            };

            loadError = originOfError;
            hdr = {
                width: gif.width,
                height: gif.height
            }; // Fake header.
            frames = [];
            drawError();

            if (originOfError)
                onErrorListener ?.(originOfError);
        };

        var doHdr = function (_hdr) {
            hdr = _hdr;
            setSizes(hdr.width, hdr.height)
        };

        var doGCE = function (gce) {
            pushFrame();
            clear();
            transparency = gce.transparencyGiven ? gce.transparencyIndex : null;
            delay = gce.delayTime;
            disposalMethod = gce.disposalMethod;
            // We don't have much to do with the rest of GCE.
        };

        var pushFrame = function () {
            if (!frame) return;
            frames.push({
                            data: frame.getImageData(0, 0, hdr.width, hdr.height),
                            delay: delay
                        });
            frameOffsets.push({ x: 0, y: 0 });
        };

        var doImg = function (img) {
            if (!frame) frame = tmpCanvas.getContext('2d', { willReadFrequently: true });

            var currIdx = frames.length;

            //ct = color table, gct = global color table
            var ct = img.lctFlag ? img.lct : hdr.gct; // TODO: What if neither exists?

            /*
            Disposal method indicates the way in which the graphic is to
            be treated after being displayed.

            Values :    0 - No disposal specified. The decoder is
                            not required to take any action.
                        1 - Do not dispose. The graphic is to be left
                            in place.
                        2 - Restore to background color. The area used by the
                            graphic must be restored to the background color.
                        3 - Restore to previous. The decoder is required to
                            restore the area overwritten by the graphic with
                            what was there prior to rendering the graphic.

                            Importantly, "previous" means the frame state
                            after the last disposal of method 0, 1, or 2.
            */
            if (currIdx > 0) {
                if (lastDisposalMethod === 3) {
                    // Restore to previous
                    // If we disposed every frame including first frame up to this point, then we have
                    // no composited frame to restore to. In this case, restore to background instead.
                    if (disposalRestoreFromIdx !== null) {
                    	frame.putImageData(frames[disposalRestoreFromIdx].data, 0, 0);
                    } else {
                    	frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
                    }
                } else {
                    disposalRestoreFromIdx = currIdx - 1;
                }

                if (lastDisposalMethod === 2) {
                    // Restore to background color
                    // Browser implementations historically restore to transparent; we do the same.
                    // http://www.wizards-toolkit.org/discourse-server/viewtopic.php?f=1&t=21172#p86079
                    frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
                }
            }
            // else, Undefined/Do not dispose.
            // frame contains final pixel data from the last frame; do nothing

            //Get existing pixels for img region after applying disposal method
            var imgData = frame.getImageData(img.leftPos, img.topPos, img.width, img.height);

            //apply color table colors
            img.pixels.forEach(function (pixel, i) {
                // imgData.data === [R,G,B,A,R,G,B,A,...]
                if (pixel !== transparency) {
                    imgData.data[i * 4 + 0] = ct[pixel][0];
                    imgData.data[i * 4 + 1] = ct[pixel][1];
                    imgData.data[i * 4 + 2] = ct[pixel][2];
                    imgData.data[i * 4 + 3] = 255; // Opaque.
                }
            });

            frame.putImageData(imgData, img.leftPos, img.topPos);

            if (!ctx_scaled) {
                ctx.scale(get_canvas_scale(),get_canvas_scale());
                ctx_scaled = true;
            }

            // We could use the on-page canvas directly, except that we draw a progress
            // bar for each image chunk (not just the final image).
            if (drawWhileLoading) {
                ctx.drawImage(tmpCanvas, 0, 0);
                drawWhileLoading = options.auto_play;
            }

            lastImg = img;
        };

        var player = (function () {
            var i = -1;
            var iterationCount = 0;

            var showingInfo = false;
            var pinned = false;

            /**
             * Gets the index of the frame "up next".
             * @returns {number}
             */
            var getNextFrameNo = function () {
                var delta = (forward ? 1 : -1);
                return (i + delta + frames.length) % frames.length;
            };

            var stepFrame = function (amount) { // XXX: Name is confusing.
                i = i + amount;

                putFrame();
            };

            var step = (function () {
                var stepping = false;

                var completeLoop = function () {
                    if (onEndListener !== null)
                        onEndListener(gif);
                    iterationCount++;

                    if (overrideLoopMode !== false || iterationCount < 0) {
                        doStep();
                    } else {
                        stepping = false;
                        playing = false;
                    }
                };

                var doStep = function () {
                    stepping = playing;
                    if (!stepping) return;

                    stepFrame(1);
                    var delay = frames[i].delay * 10;
                    if (!delay) delay = 100; // FIXME: Should this even default at all? What should it be?

                    var nextFrameNo = getNextFrameNo();
                    if (nextFrameNo === 0) {
                        delay += loopDelay;
                        setTimeout(completeLoop, delay);
                    } else {
                        setTimeout(doStep, delay);
                    }
                };

                return function () {
                    if (!stepping) setTimeout(doStep, 0);
                };
            }());

            var putFrame = function () {
                var offset;
                i = parseInt(i, 10);

                if (i > frames.length - 1){
                    i = 0;
                }

                if (i < 0){
                    i = 0;
                }

                offset = frameOffsets[i];

                tmpCanvas.getContext("2d", { willReadFrequently: true }).putImageData(frames[i].data, offset.x, offset.y);
                ctx.globalCompositeOperation = "copy";
                ctx.drawImage(tmpCanvas, 0, 0);
            };

            var play = function () {
                playing = true;
                step();
            };

            var pause = function () {
                playing = false;
            };


            return {
                init: function () {
                    if (loadError) return;

                    if ( ! (options.c_w && options.c_h) ) {
                        ctx.scale(get_canvas_scale(),get_canvas_scale());
                    }

                    if (options.auto_play) {
                        step();
                    }
                    else {
                        i = 0;
                        putFrame();
                    }
                },
                step: step,
                play: play,
                pause: pause,
                playing: playing,
                move_relative: stepFrame,
                current_frame: function() { return i; },
                length: function() { return frames.length },
                move_to: function ( frame_idx ) {
                    i = frame_idx;
                    putFrame();
                }
            }
        }());

        var doDecodeProgress = function (draw) {
            doShowProgress(stream.pos, stream.data.length, draw);
        };

        var doNothing = function () {};
        /**
         * @param{boolean=} draw Whether to draw progress bar or not; this is not idempotent because of translucency.
         *                       Note that this means that the text will be unsynchronized with the progress bar on non-frames;
         *                       but those are typically so small (GCE etc.) that it doesn't really matter. TODO: Do this properly.
         */
        var withProgress = function (fn, draw) {
            return function (block) {
                fn(block);
                doDecodeProgress(draw);
            };
        };


        var handler = {
            hdr: withProgress(doHdr),
            gce: withProgress(doGCE),
            com: withProgress(doNothing),
            // I guess that's all for now.
            app: {
                // TODO: Is there much point in actually supporting iterations?
                NETSCAPE: withProgress(doNothing)
            },
            img: withProgress(doImg, true),
            eof: function (block) {
                //toolbar.style.display = '';
                pushFrame();
                doDecodeProgress(false);
                if ( ! (options.c_w && options.c_h) ) {
                    canvas.width = hdr.width * get_canvas_scale();
                    canvas.height = hdr.height * get_canvas_scale();
                }
                player.init();
                loading = false;
                if (load_callback) {
                    load_callback(gif);
                }

            }
        };

        var init = function () {
            var parent = gif.parentNode;

            var div = document.createElement('div');
            canvas = document.createElement('canvas');
            ctx = canvas.getContext('2d', { willReadFrequently: true });
            toolbar = document.createElement('div');

            tmpCanvas = document.createElement('canvas');

            div.width = canvas.width = gif.width;
            div.height = canvas.height = gif.height;
            toolbar.style.minWidth = gif.width + 'px';

            div.className = 'jsgif';
            toolbar.className = 'jsgif_toolbar';
            div.appendChild(canvas);
            div.appendChild(toolbar);

            parent.insertBefore(div, gif);
            parent.removeChild(gif);

            if (options.c_w && options.c_h) setSizes(options.c_w, options.c_h);
            initialized=true;
        };

        var get_canvas_scale = function() {
            var scale;
            if (options.max_width && hdr && hdr.width > options.max_width) {
                scale = options.max_width / hdr.width;
            }
            else {
                scale = 1;
            }
            return scale;
        }

        var canvas, ctx, toolbar, tmpCanvas;
        var initialized = false;
        var load_callback = false;

        var load_setup = function(callback) {
            if (loading) return false;
            if (callback) load_callback = callback;
            else load_callback = false;

            loading = true;
            frames = [];
            clear();
            disposalRestoreFromIdx = null;
            lastDisposalMethod = null;
            frame = null;
            lastImg = null;

            return true;
        }

        var calculateDuration = function() {
            return frames.reduce(function(duration, frame) {
                return duration + frame.delay;
            }, 0);
        }

        return {
            // play controls
            play: player.play,
            pause: player.pause,
            move_relative: player.move_relative,
            move_to: player.move_to,

            // getters for instance vars
            get_playing      : function() { return playing },
            get_canvas       : function() { return canvas },
            get_canvas_scale : function() { return get_canvas_scale() },
            get_loading      : function() { return loading },
            get_auto_play    : function() { return options.auto_play },
            get_length       : function() { return player.length() },
            get_frames       : function() { return frames },
            get_duration     : function() { return calculateDuration() },
            get_duration_ms  : function() { return calculateDuration() * 10 },
            get_current_frame: function() { return player.current_frame() },
            load_url: function(src,callback){
                if (!load_setup(callback)) return;

                var h = new XMLHttpRequest();
                // new browsers (XMLHttpRequest2-compliant)
                h.open('GET', src, true);

                if ('overrideMimeType' in h) {
                    h.overrideMimeType('text/plain; charset=x-user-defined');
                }

                // old browsers (XMLHttpRequest-compliant)
                else if ('responseType' in h) {
                    h.responseType = 'arraybuffer';
                }

                // IE9 (Microsoft.XMLHTTP-compliant)
                else {
                    h.setRequestHeader('Accept-Charset', 'x-user-defined');
                }

                h.onloadstart = function() {
                    // Wait until connection is opened to replace the gif element with a canvas to avoid a blank img
                    if (!initialized) init();
                };
                h.onload = function(e) {
                    if (this.status != 200) {
                        doLoadError('xhr - response');
                    }
                    // emulating response field for IE9
                    if (!('response' in this)) {
                        this.response = new VBArray(this.responseText).toArray().map(String.fromCharCode).join('');
                    }
                    var data = this.response;
                    if (data.toString().indexOf("ArrayBuffer") > 0) {
                        data = new Uint8Array(data);
                    }

                    stream = new Stream(data);
                    setTimeout(doParse, 0);
                };
                h.onprogress = function (e) {
                    if (e.lengthComputable) doShowProgress(e.loaded, e.total, true);
                };
                h.onerror = function() { doLoadError('xhr'); };
                h.send();
            },
            load: function (callback) {
                this.load_url(gif.getAttribute('rel:animated_src') || gif.src,callback);
            },
            load_raw: function(arr, callback) {
                if (!load_setup(callback)) return;
                if (!initialized) init();
                stream = new Stream(arr);
                setTimeout(doParse, 0);
            },
            set_frame_offset: setFrameOffset
        };
    };

    return SuperGif;
}));




/***/ },

/***/ 364
(module) {

(function webpackUniversalModuleDefinition(root, factory) {
	if(true)
		module.exports = factory();
	else // removed by dead control flow
{}
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __nested_webpack_require_529__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __nested_webpack_require_529__);

/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}


/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__nested_webpack_require_529__.m = modules;

/******/ 	// expose the module cache
/******/ 	__nested_webpack_require_529__.c = installedModules;

/******/ 	// __webpack_public_path__
/******/ 	__nested_webpack_require_529__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __nested_webpack_require_529__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __nested_webpack_require_1730__) {

	'use strict';

	Object.defineProperty(exports, "__esModule", {
	    value: true
	});
	exports.isNotPNG = isNotPNG;
	exports.isNotAPNG = isNotAPNG;
	exports.default = parseAPNG;

	var _crc = __nested_webpack_require_1730__(1);

	var _crc2 = _interopRequireDefault(_crc);

	var _structs = __nested_webpack_require_1730__(2);

	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

	var errNotPNG = new Error('Not a PNG');
	var errNotAPNG = new Error('Not an animated PNG');

	function isNotPNG(err) {
	    return err === errNotPNG;
	}
	function isNotAPNG(err) {
	    return err === errNotAPNG;
	}

	// '\x89PNG\x0d\x0a\x1a\x0a'
	var PNGSignature = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);

	/**
	 * Parse APNG data
	 * @param {ArrayBuffer} buffer
	 * @return {APNG|Error}
	 */
	function parseAPNG(buffer) {
	    var bytes = new Uint8Array(buffer);

	    if (Array.prototype.some.call(PNGSignature, function (b, i) {
	        return b !== bytes[i];
	    })) {
	        return errNotPNG;
	    }

	    // fast animation test
	    var isAnimated = false;
	    eachChunk(bytes, function (type) {
	        return !(isAnimated = type === 'acTL');
	    });
	    if (!isAnimated) {
	        return errNotAPNG;
	    }

	    var preDataParts = [],
	        postDataParts = [];
	    var headerDataBytes = null,
	        frame = null,
	        frameNumber = 0,
	        apng = new _structs.APNG();

	    eachChunk(bytes, function (type, bytes, off, length) {
	        var dv = new DataView(bytes.buffer);
	        switch (type) {
	            case 'IHDR':
	                headerDataBytes = bytes.subarray(off + 8, off + 8 + length);
	                apng.width = dv.getUint32(off + 8);
	                apng.height = dv.getUint32(off + 12);
	                break;
	            case 'acTL':
	                apng.numPlays = dv.getUint32(off + 8 + 4);
	                break;
	            case 'fcTL':
	                if (frame) {
	                    apng.frames.push(frame);
	                    frameNumber++;
	                }
	                frame = new _structs.Frame();
	                frame.width = dv.getUint32(off + 8 + 4);
	                frame.height = dv.getUint32(off + 8 + 8);
	                frame.left = dv.getUint32(off + 8 + 12);
	                frame.top = dv.getUint32(off + 8 + 16);
	                var delayN = dv.getUint16(off + 8 + 20);
	                var delayD = dv.getUint16(off + 8 + 22);
	                if (delayD === 0) {
	                    delayD = 100;
	                }
	                frame.delay = 1000 * delayN / delayD;
	                // https://bugzilla.mozilla.org/show_bug.cgi?id=125137
	                // https://bugzilla.mozilla.org/show_bug.cgi?id=139677
	                // https://bugzilla.mozilla.org/show_bug.cgi?id=207059
	                if (frame.delay <= 10) {
	                    frame.delay = 100;
	                }
	                apng.playTime += frame.delay;
	                frame.disposeOp = dv.getUint8(off + 8 + 24);
	                frame.blendOp = dv.getUint8(off + 8 + 25);
	                frame.dataParts = [];
	                if (frameNumber === 0 && frame.disposeOp === 2) {
	                    frame.disposeOp = 1;
	                }
	                break;
	            case 'fdAT':
	                if (frame) {
	                    frame.dataParts.push(bytes.subarray(off + 8 + 4, off + 8 + length));
	                }
	                break;
	            case 'IDAT':
	                if (frame) {
	                    frame.dataParts.push(bytes.subarray(off + 8, off + 8 + length));
	                }
	                break;
	            case 'IEND':
	                postDataParts.push(subBuffer(bytes, off, 12 + length));
	                break;
	            default:
	                preDataParts.push(subBuffer(bytes, off, 12 + length));
	        }
	    });

	    if (frame) {
	        apng.frames.push(frame);
	    }

	    if (apng.frames.length == 0) {
	        return errNotAPNG;
	    }

	    var preBlob = new Blob(preDataParts),
	        postBlob = new Blob(postDataParts);

	    apng.frames.forEach(function (frame) {
	        var bb = [];
	        bb.push(PNGSignature);
	        headerDataBytes.set(makeDWordArray(frame.width), 0);
	        headerDataBytes.set(makeDWordArray(frame.height), 4);
	        bb.push(makeChunkBytes('IHDR', headerDataBytes));
	        bb.push(preBlob);
	        frame.dataParts.forEach(function (p) {
	            return bb.push(makeChunkBytes('IDAT', p));
	        });
	        bb.push(postBlob);
	        frame.imageData = new Blob(bb, { 'type': 'image/png' });
	        delete frame.dataParts;
	        bb = null;
	    });

	    return apng;
	}

	/**
	 * @param {Uint8Array} bytes
	 * @param {function(string, Uint8Array, int, int): boolean} callback
	 */
	function eachChunk(bytes, callback) {
	    var dv = new DataView(bytes.buffer);
	    var off = 8,
	        type = void 0,
	        length = void 0,
	        res = void 0;
	    do {
	        length = dv.getUint32(off);
	        type = readString(bytes, off + 4, 4);
	        res = callback(type, bytes, off, length);
	        off += 12 + length;
	    } while (res !== false && type != 'IEND' && off < bytes.length);
	}

	/**
	 *
	 * @param {Uint8Array} bytes
	 * @param {number} off
	 * @param {number} length
	 * @return {string}
	 */
	function readString(bytes, off, length) {
	    var chars = Array.prototype.slice.call(bytes.subarray(off, off + length));
	    return String.fromCharCode.apply(String, chars);
	}

	/**
	 *
	 * @param {string} x
	 * @return {Uint8Array}
	 */
	function makeStringArray(x) {
	    var res = new Uint8Array(x.length);
	    for (var i = 0; i < x.length; i++) {
	        res[i] = x.charCodeAt(i);
	    }
	    return res;
	}

	/**
	 * @param {Uint8Array} bytes
	 * @param {int} start
	 * @param {int} length
	 * @return {Uint8Array}
	 */
	function subBuffer(bytes, start, length) {
	    var a = new Uint8Array(length);
	    a.set(bytes.subarray(start, start + length));
	    return a;
	}

	/**
	 * @param {string} type
	 * @param {Uint8Array} dataBytes
	 * @return {Uint8Array}
	 */
	var makeChunkBytes = function makeChunkBytes(type, dataBytes) {
	    var crcLen = type.length + dataBytes.length;
	    var bytes = new Uint8Array(crcLen + 8);
	    var dv = new DataView(bytes.buffer);

	    dv.setUint32(0, dataBytes.length);
	    bytes.set(makeStringArray(type), 4);
	    bytes.set(dataBytes, 8);
	    var crc = (0, _crc2.default)(bytes, 4, crcLen);
	    dv.setUint32(crcLen + 4, crc);
	    return bytes;
	};

	var makeDWordArray = function makeDWordArray(x) {
	    return new Uint8Array([x >>> 24 & 0xff, x >>> 16 & 0xff, x >>> 8 & 0xff, x & 0xff]);
	};

/***/ }),
/* 1 */
/***/ (function(module, exports) {

	"use strict";

	Object.defineProperty(exports, "__esModule", {
	  value: true
	});

	exports.default = function (bytes) {
	  var start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
	  var length = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : bytes.length - start;

	  var crc = -1;
	  for (var _i = start, l = start + length; _i < l; _i++) {
	    crc = crc >>> 8 ^ table[(crc ^ bytes[_i]) & 0xFF];
	  }
	  return crc ^ -1;
	};

	var table = new Uint32Array(256);

	for (var i = 0; i < 256; i++) {
	  var c = i;
	  for (var k = 0; k < 8; k++) {
	    c = (c & 1) !== 0 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;
	  }
	  table[i] = c;
	}

	/**
	 *
	 * @param {Uint8Array} bytes
	 * @param {number} start
	 * @param {number} length
	 * @return {number}
	 */

/***/ }),
/* 2 */
/***/ (function(module, exports, __nested_webpack_require_9403__) {

	'use strict';

	Object.defineProperty(exports, "__esModule", {
	    value: true
	});
	exports.Frame = exports.APNG = undefined;

	var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

	var _player = __nested_webpack_require_9403__(3);

	var _player2 = _interopRequireDefault(_player);

	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

	/**
	 * @property {number} currFrameNumber
	 * @property {Frame} currFrame
	 * @property {boolean} paused
	 * @property {boolean} ended
	 */
	var APNG = exports.APNG = function () {
	    function APNG() {
	        _classCallCheck(this, APNG);

	        this.width = 0;
	        this.height = 0;
	        this.numPlays = 0;
	        this.playTime = 0;
	        this.frames = [];
	    }
	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {Frame[]} */


	    _createClass(APNG, [{
	        key: 'createImages',


	        /**
	         *
	         * @return {Promise.<*>}
	         */
	        value: function createImages() {
	            return Promise.all(this.frames.map(function (f) {
	                return f.createImage();
	            }));
	        }

	        /**
	         *
	         * @param {CanvasRenderingContext2D} context
	         * @param {boolean} autoPlay
	         * @return {Promise.<Player>}
	         */

	    }, {
	        key: 'getPlayer',
	        value: function getPlayer(context) {
	            var _this = this;

	            var autoPlay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

	            return this.createImages().then(function () {
	                return new _player2.default(_this, context, autoPlay);
	            });
	        }
	    }]);

	    return APNG;
	}();

	var Frame = exports.Frame = function () {
	    function Frame() {
	        _classCallCheck(this, Frame);

	        this.left = 0;
	        this.top = 0;
	        this.width = 0;
	        this.height = 0;
	        this.delay = 0;
	        this.disposeOp = 0;
	        this.blendOp = 0;
	        this.imageData = null;
	        this.imageElement = null;
	    }
	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {number} */

	    /** @type {Blob} */

	    /** @type {HTMLImageElement} */


	    _createClass(Frame, [{
	        key: 'createImage',
	        value: function createImage() {
	            var _this2 = this;

	            if (this.imageElement) {
	                return Promise.resolve();
	            }
	            return new Promise(function (resolve, reject) {
	                var url = URL.createObjectURL(_this2.imageData);
	                _this2.imageElement = document.createElement('img');
	                _this2.imageElement.onload = function () {
	                    URL.revokeObjectURL(url);
	                    resolve();
	                };
	                _this2.imageElement.onerror = function () {
	                    URL.revokeObjectURL(url);
	                    _this2.imageElement = null;
	                    reject(new Error("Image creation error"));
	                };
	                _this2.imageElement.src = url;
	            });
	        }
	    }]);

	    return Frame;
	}();

/***/ }),
/* 3 */
/***/ (function(module, exports, __nested_webpack_require_13509__) {

	'use strict';

	Object.defineProperty(exports, "__esModule", {
	    value: true
	});

	var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

	var _events = __nested_webpack_require_13509__(4);

	var _events2 = _interopRequireDefault(_events);

	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

	function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

	function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

	var _class = function (_EventEmitter) {
	    _inherits(_class, _EventEmitter);

	    /**
	     * @param {APNG} apng
	     * @param {CanvasRenderingContext2D} context
	     * @param {boolean} autoPlay
	     */

	    /** @type {number} */


	    /** @type {boolean} */

	    /** @type {ImageData} */


	    /** @type {APNG} */

	    /** @type {CanvasRenderingContext2D} */
	    function _class(apng, context, autoPlay) {
	        _classCallCheck(this, _class);

	        var _this = _possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).call(this));

	        _this.playbackRate = 1.0;
	        _this._currentFrameNumber = 0;
	        _this._ended = false;
	        _this._paused = true;
	        _this._numPlays = 0;
	        _this._rafId = null;

	        _this._apng = apng;
	        _this.context = context;
	        _this.stop();
	        if (autoPlay) {
	            _this.play();
	        }
	        return _this;
	    }

	    /**
	     *
	     * @return {number}
	     */

	    /** @type {number|null} */

	    /** @type {boolean} */

	    /** @type {number} */

	    /** @type {Frame} */

	    /** @type {number} */


	    _createClass(_class, [{
	        key: 'renderNextFrame',
	        value: function renderNextFrame() {
	            this._currentFrameNumber = (this._currentFrameNumber + 1) % this._apng.frames.length;
	            if (this._currentFrameNumber === this._apng.frames.length - 1) {
	                this._numPlays++;
	                if (this._apng.numPlays !== 0 && this._numPlays >= this._apng.numPlays) {
	                    this._ended = true;
	                    this._paused = true;
	                }
	            }

	            if (this._prevFrame && this._prevFrame.disposeOp == 1) {
	                this.context.clearRect(this._prevFrame.left, this._prevFrame.top, this._prevFrame.width, this._prevFrame.height);
	            } else if (this._prevFrame && this._prevFrame.disposeOp == 2) {
	                this.context.putImageData(this._prevFrameData, this._prevFrame.left, this._prevFrame.top);
	            }

	            var frame = this.currentFrame;
	            this._prevFrame = frame;
	            this._prevFrameData = null;
	            if (frame.disposeOp == 2) {
	                this._prevFrameData = this.context.getImageData(frame.left, frame.top, frame.width, frame.height);
	            }
	            if (frame.blendOp == 0) {
	                this.context.clearRect(frame.left, frame.top, frame.width, frame.height);
	            }

	            this.context.drawImage(frame.imageElement, frame.left, frame.top);

	            this.emit('frame', this._currentFrameNumber);
	            if (this._ended) {
	                this.emit('end');
	            }
	        }

	        // playback

	    }, {
	        key: 'play',
	        value: function play() {
	            var _this2 = this;

	            if (this._rafId) {
	                cancelAnimationFrame(this._rafId);
	            }

	            this.emit('play');

	            if (this._ended) {
	                this.stop();
	            }
	            this._paused = false;

	            var nextRenderTime = performance.now() + this.currentFrame.delay / this.playbackRate;
	            var tick = function tick(now) {
	                if (_this2._ended || _this2._paused) {
	                    return;
	                }
	                if (now >= nextRenderTime) {
	                    while (now - nextRenderTime >= _this2._apng.playTime / _this2.playbackRate) {
	                        nextRenderTime += _this2._apng.playTime / _this2.playbackRate;
	                        _this2._numPlays++;
	                    }
	                    do {
	                        _this2.renderNextFrame();
	                        nextRenderTime += _this2.currentFrame.delay / _this2.playbackRate;
	                    } while (!_this2._ended && !_this2._paused && now > nextRenderTime);
	                }
	                _this2._rafId = requestAnimationFrame(tick);
	            };
	            this._rafId = requestAnimationFrame(tick);
	        }
	    }, {
	        key: 'pause',
	        value: function pause() {
	            if (!this._paused) {
	                if (this._rafId) {
	                    cancelAnimationFrame(this._rafId);
	                    this._rafId = null;
	                }
	                this.emit('pause');
	                this._paused = true;
	            }
	        }
	    }, {
	        key: 'stop',
	        value: function stop() {
	            if (this._rafId) {
	                cancelAnimationFrame(this._rafId);
	                this._rafId = null;
	            }
	            this.emit('stop');
	            this._numPlays = 0;
	            this._ended = false;
	            this._paused = true;
	            // render first frame
	            this._currentFrameNumber = -1;
	            this.context.clearRect(0, 0, this._apng.width, this._apng.height);
	            this.renderNextFrame();
	        }
	    }, {
	        key: 'currentFrameNumber',
	        get: function get() {
	            return this._currentFrameNumber;
	        }

	        /**
	         *
	         * @return {Frame}
	         */

	    }, {
	        key: 'currentFrame',
	        get: function get() {
	            return this._apng.frames[this._currentFrameNumber];
	        }
	    }, {
	        key: 'paused',
	        get: function get() {
	            return this._paused;
	        }
	    }, {
	        key: 'ended',
	        get: function get() {
	            return this._ended;
	        }
	    }]);

	    return _class;
	}(_events2.default);

	exports.default = _class;

/***/ }),
/* 4 */
/***/ (function(module, exports) {

	// Copyright Joyent, Inc. and other Node contributors.
	//
	// Permission is hereby granted, free of charge, to any person obtaining a
	// copy of this software and associated documentation files (the
	// "Software"), to deal in the Software without restriction, including
	// without limitation the rights to use, copy, modify, merge, publish,
	// distribute, sublicense, and/or sell copies of the Software, and to permit
	// persons to whom the Software is furnished to do so, subject to the
	// following conditions:
	//
	// The above copyright notice and this permission notice shall be included
	// in all copies or substantial portions of the Software.
	//
	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
	// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
	// USE OR OTHER DEALINGS IN THE SOFTWARE.

	function EventEmitter() {
	  this._events = this._events || {};
	  this._maxListeners = this._maxListeners || undefined;
	}
	module.exports = EventEmitter;

	// Backwards-compat with node 0.10.x
	EventEmitter.EventEmitter = EventEmitter;

	EventEmitter.prototype._events = undefined;
	EventEmitter.prototype._maxListeners = undefined;

	// By default EventEmitters will print a warning if more than 10 listeners are
	// added to it. This is a useful default which helps finding memory leaks.
	EventEmitter.defaultMaxListeners = 10;

	// Obviously not all Emitters should be limited to 10. This function allows
	// that to be increased. Set to zero for unlimited.
	EventEmitter.prototype.setMaxListeners = function(n) {
	  if (!isNumber(n) || n < 0 || isNaN(n))
	    throw TypeError('n must be a positive number');
	  this._maxListeners = n;
	  return this;
	};

	EventEmitter.prototype.emit = function(type) {
	  var er, handler, len, args, i, listeners;

	  if (!this._events)
	    this._events = {};

	  // If there is no 'error' event listener then throw.
	  if (type === 'error') {
	    if (!this._events.error ||
	        (isObject(this._events.error) && !this._events.error.length)) {
	      er = arguments[1];
	      if (er instanceof Error) {
	        throw er; // Unhandled 'error' event
	      } else {
	        // At least give some kind of context to the user
	        var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
	        err.context = er;
	        throw err;
	      }
	    }
	  }

	  handler = this._events[type];

	  if (isUndefined(handler))
	    return false;

	  if (isFunction(handler)) {
	    switch (arguments.length) {
	      // fast cases
	      case 1:
	        handler.call(this);
	        break;
	      case 2:
	        handler.call(this, arguments[1]);
	        break;
	      case 3:
	        handler.call(this, arguments[1], arguments[2]);
	        break;
	      // slower
	      default:
	        args = Array.prototype.slice.call(arguments, 1);
	        handler.apply(this, args);
	    }
	  } else if (isObject(handler)) {
	    args = Array.prototype.slice.call(arguments, 1);
	    listeners = handler.slice();
	    len = listeners.length;
	    for (i = 0; i < len; i++)
	      listeners[i].apply(this, args);
	  }

	  return true;
	};

	EventEmitter.prototype.addListener = function(type, listener) {
	  var m;

	  if (!isFunction(listener))
	    throw TypeError('listener must be a function');

	  if (!this._events)
	    this._events = {};

	  // To avoid recursion in the case that type === "newListener"! Before
	  // adding it to the listeners, first emit "newListener".
	  if (this._events.newListener)
	    this.emit('newListener', type,
	              isFunction(listener.listener) ?
	              listener.listener : listener);

	  if (!this._events[type])
	    // Optimize the case of one listener. Don't need the extra array object.
	    this._events[type] = listener;
	  else if (isObject(this._events[type]))
	    // If we've already got an array, just append.
	    this._events[type].push(listener);
	  else
	    // Adding the second element, need to change to array.
	    this._events[type] = [this._events[type], listener];

	  // Check for listener leak
	  if (isObject(this._events[type]) && !this._events[type].warned) {
	    if (!isUndefined(this._maxListeners)) {
	      m = this._maxListeners;
	    } else {
	      m = EventEmitter.defaultMaxListeners;
	    }

	    if (m && m > 0 && this._events[type].length > m) {
	      this._events[type].warned = true;
	      console.error('(node) warning: possible EventEmitter memory ' +
	                    'leak detected. %d listeners added. ' +
	                    'Use emitter.setMaxListeners() to increase limit.',
	                    this._events[type].length);
	      if (typeof console.trace === 'function') {
	        // not supported in IE 10
	        console.trace();
	      }
	    }
	  }

	  return this;
	};

	EventEmitter.prototype.on = EventEmitter.prototype.addListener;

	EventEmitter.prototype.once = function(type, listener) {
	  if (!isFunction(listener))
	    throw TypeError('listener must be a function');

	  var fired = false;

	  function g() {
	    this.removeListener(type, g);

	    if (!fired) {
	      fired = true;
	      listener.apply(this, arguments);
	    }
	  }

	  g.listener = listener;
	  this.on(type, g);

	  return this;
	};

	// emits a 'removeListener' event iff the listener was removed
	EventEmitter.prototype.removeListener = function(type, listener) {
	  var list, position, length, i;

	  if (!isFunction(listener))
	    throw TypeError('listener must be a function');

	  if (!this._events || !this._events[type])
	    return this;

	  list = this._events[type];
	  length = list.length;
	  position = -1;

	  if (list === listener ||
	      (isFunction(list.listener) && list.listener === listener)) {
	    delete this._events[type];
	    if (this._events.removeListener)
	      this.emit('removeListener', type, listener);

	  } else if (isObject(list)) {
	    for (i = length; i-- > 0;) {
	      if (list[i] === listener ||
	          (list[i].listener && list[i].listener === listener)) {
	        position = i;
	        break;
	      }
	    }

	    if (position < 0)
	      return this;

	    if (list.length === 1) {
	      list.length = 0;
	      delete this._events[type];
	    } else {
	      list.splice(position, 1);
	    }

	    if (this._events.removeListener)
	      this.emit('removeListener', type, listener);
	  }

	  return this;
	};

	EventEmitter.prototype.removeAllListeners = function(type) {
	  var key, listeners;

	  if (!this._events)
	    return this;

	  // not listening for removeListener, no need to emit
	  if (!this._events.removeListener) {
	    if (arguments.length === 0)
	      this._events = {};
	    else if (this._events[type])
	      delete this._events[type];
	    return this;
	  }

	  // emit removeListener for all listeners on all events
	  if (arguments.length === 0) {
	    for (key in this._events) {
	      if (key === 'removeListener') continue;
	      this.removeAllListeners(key);
	    }
	    this.removeAllListeners('removeListener');
	    this._events = {};
	    return this;
	  }

	  listeners = this._events[type];

	  if (isFunction(listeners)) {
	    this.removeListener(type, listeners);
	  } else if (listeners) {
	    // LIFO order
	    while (listeners.length)
	      this.removeListener(type, listeners[listeners.length - 1]);
	  }
	  delete this._events[type];

	  return this;
	};

	EventEmitter.prototype.listeners = function(type) {
	  var ret;
	  if (!this._events || !this._events[type])
	    ret = [];
	  else if (isFunction(this._events[type]))
	    ret = [this._events[type]];
	  else
	    ret = this._events[type].slice();
	  return ret;
	};

	EventEmitter.prototype.listenerCount = function(type) {
	  if (this._events) {
	    var evlistener = this._events[type];

	    if (isFunction(evlistener))
	      return 1;
	    else if (evlistener)
	      return evlistener.length;
	  }
	  return 0;
	};

	EventEmitter.listenerCount = function(emitter, type) {
	  return emitter.listenerCount(type);
	};

	function isFunction(arg) {
	  return typeof arg === 'function';
	}

	function isNumber(arg) {
	  return typeof arg === 'number';
	}

	function isObject(arg) {
	  return typeof arg === 'object' && arg !== null;
	}

	function isUndefined(arg) {
	  return arg === void 0;
	}


/***/ })
/******/ ])
});
;

/***/ },

/***/ 395
(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
 * jQuery Mousewheel 3.2.2
 * Copyright OpenJS Foundation and other contributors
 */

( function( factory ) {
    "use strict";

    if ( true ) {

        // AMD. Register as an anonymous module.
        !(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(228) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
		__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
		(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
		__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    } else // removed by dead control flow
{}
} )( function( $ ) {
    "use strict";

    var nullLowestDeltaTimeout, lowestDelta,
        modernEvents = !!$.fn.on,
        toFix  = [ "wheel", "mousewheel", "DOMMouseScroll", "MozMousePixelScroll" ],
        toBind = ( "onwheel" in window.document || window.document.documentMode >= 9 ) ?
            [ "wheel" ] : [ "mousewheel", "DomMouseScroll", "MozMousePixelScroll" ],
        slice  = Array.prototype.slice;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[ --i ] ] = $.event.mouseHooks;
        }
    }

    var special = $.event.special.mousewheel = {
        version: "3.2.2",

        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[ --i ], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }

            // Store the line height and page height for this particular element
            $.data( this, "mousewheel-line-height", special.getLineHeight( this ) );
            $.data( this, "mousewheel-page-height", special.getPageHeight( this ) );
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[ --i ], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }

            // Clean up the data we added to the element
            $.removeData( this, "mousewheel-line-height" );
            $.removeData( this, "mousewheel-page-height" );
        },

        getLineHeight: function( elem ) {
            var $elem = $( elem ),
                $parent = $elem[ "offsetParent" in $.fn ? "offsetParent" : "parent" ]();
            if ( !$parent.length ) {
                $parent = $( "body" );
            }
            return parseInt( $parent.css( "fontSize" ), 10 ) ||
                parseInt( $elem.css( "fontSize" ), 10 ) || 16;
        },

        getPageHeight: function( elem ) {
            return $( elem ).height();
        },

        settings: {
            adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
            normalizeOffset: true  // calls getBoundingClientRect for each event
        }
    };

    $.fn.extend( {
        mousewheel: function( fn ) {
            return fn ?
                this[ modernEvents ? "on" : "bind" ]( "mousewheel", fn ) :
                this.trigger( "mousewheel" );
        },

        unmousewheel: function( fn ) {
            return this[ modernEvents ? "off" : "unbind" ]( "mousewheel", fn );
        }
    } );


    function handler( event ) {
        var orgEvent   = event || window.event,
            args       = slice.call( arguments, 1 ),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0;
        event = $.event.fix( orgEvent );
        event.type = "mousewheel";

        // Old school scrollwheel delta
        if ( "detail" in orgEvent ) {
            deltaY = orgEvent.detail * -1;
        }
        if ( "wheelDelta" in orgEvent ) {
            deltaY = orgEvent.wheelDelta;
        }
        if ( "wheelDeltaY" in orgEvent ) {
            deltaY = orgEvent.wheelDeltaY;
        }
        if ( "wheelDeltaX" in orgEvent ) {
            deltaX = orgEvent.wheelDeltaX * -1;
        }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( "axis" in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaX = deltaY * -1;
            deltaY = 0;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatability
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( "deltaY" in orgEvent ) {
            deltaY = orgEvent.deltaY * -1;
            delta  = deltaY;
        }
        if ( "deltaX" in orgEvent ) {
            deltaX = orgEvent.deltaX;
            if ( deltaY === 0 ) {
                delta  = deltaX * -1;
            }
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) {
            return;
        }

        // Need to convert lines and pages to pixels if we aren't already in pixels
        // There are three delta modes:
        //   * deltaMode 0 is by pixels, nothing to do
        //   * deltaMode 1 is by lines
        //   * deltaMode 2 is by pages
        if ( orgEvent.deltaMode === 1 ) {
            var lineHeight = $.data( this, "mousewheel-line-height" );
            delta  *= lineHeight;
            deltaY *= lineHeight;
            deltaX *= lineHeight;
        } else if ( orgEvent.deltaMode === 2 ) {
            var pageHeight = $.data( this, "mousewheel-page-height" );
            delta  *= pageHeight;
            deltaY *= pageHeight;
            deltaX *= pageHeight;
        }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs( deltaY ), Math.abs( deltaX ) );

        if ( !lowestDelta || absDelta < lowestDelta ) {
            lowestDelta = absDelta;

            // Adjust older deltas if necessary
            if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {
                lowestDelta /= 40;
            }
        }

        // Adjust older deltas if necessary
        if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {

            // Divide all the things by 40!
            delta  /= 40;
            deltaX /= 40;
            deltaY /= 40;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? "floor" : "ceil" ]( delta  / lowestDelta );
        deltaX = Math[ deltaX >= 1 ? "floor" : "ceil" ]( deltaX / lowestDelta );
        deltaY = Math[ deltaY >= 1 ? "floor" : "ceil" ]( deltaY / lowestDelta );

        // Normalise offsetX and offsetY properties
        if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
            var boundingRect = this.getBoundingClientRect();
            event.offsetX = event.clientX - boundingRect.left;
            event.offsetY = event.clientY - boundingRect.top;
        }

        // Add information to the event object
        event.deltaX = deltaX;
        event.deltaY = deltaY;
        event.deltaFactor = lowestDelta;

        // Go ahead and set deltaMode to 0 since we converted to pixels
        // Although this is a little odd since we overwrite the deltaX/Y
        // properties with normalized deltas.
        event.deltaMode = 0;

        // Add event and delta to the front of the arguments
        args.unshift( event, delta, deltaX, deltaY );

        // Clear out lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        if ( nullLowestDeltaTimeout ) {
            window.clearTimeout( nullLowestDeltaTimeout );
        }
        nullLowestDeltaTimeout = window.setTimeout( function() {
            lowestDelta = null;
        }, 200 );

        return ( $.event.dispatch || $.event.handle ).apply( this, args );
    }

    function shouldAdjustOldDeltas( orgEvent, absDelta ) {

        // If this is an older event and the delta is divisible by 120,
        // then we are assuming that the browser is treating this as an
        // older mouse wheel event and that we should divide the deltas
        // by 40 to try and get a more usable deltaFactor.
        // Side note, this actually impacts the reported scroll distance
        // in older browsers and can cause scrolling to be slower than native.
        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
        return special.settings.adjustOldDeltas && orgEvent.type === "mousewheel" &&
            absDelta % 120 === 0;
    }

} );


/***/ },

/***/ 872
(module) {

/**
 * @license
 * Copyright 2000, Silicon Graphics, Inc. All Rights Reserved.
 * Copyright 2015, Google Inc. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice including the dates of first publication and
 * either this permission notice or a reference to http://oss.sgi.com/projects/FreeB/
 * shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Original Code. The Original Code is: OpenGL Sample Implementation,
 * Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
 * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
 * Copyright in any portions created by third parties is as indicated
 * elsewhere herein. All Rights Reserved.
 */
/**
 * @author ericv@cs.stanford.edu (Eric Veach)
 * @author bckenny@google.com (Brendan Kenny)
 */

/**
 * Base namespace.
 * @const
 */
var libtess = {};

/**
 * Whether to run asserts and extra debug checks.
 * @define {boolean}
 */
libtess.DEBUG = false;

/**
 * Checks if the condition evaluates to true if libtess.DEBUG is true.
 * @param {*} condition The condition to check.
 * @param {string=} opt_message Error message in case of failure.
 * @throws {Error} Assertion failed, the condition evaluates to false.
 */
libtess.assert = function(condition, opt_message) {
  if (libtess.DEBUG && !condition) {
    throw new Error('Assertion failed' +
        (opt_message ? ': ' + opt_message : ''));
  }
};

/**
 * The maximum vertex coordinate size, 1e150. Anything larger will trigger a
 * GLU_TESS_COORD_TOO_LARGE error callback and the vertex will be clamped to
 * this value for all tessellation calculations.
 * @const {number}
 */
libtess.GLU_TESS_MAX_COORD = 1e150;
// NOTE(bckenny): value from glu.pl generator

/**
 * Normally the polygon is projected to a plane perpendicular to one of the
 * three coordinate axes before tessellating in 2d. This helps numerical
 * accuracy by forgoing a transformation step by simply dropping one coordinate
 * dimension.
 *
 * However, this can affect the placement of intersection points for non-axis-
 * aligned polygons. Setting TRUE_PROJECT to true will instead project onto a
 * plane actually perpendicular to the polygon's normal.
 *
 * NOTE(bckenny): I can find no instances on the internet in which this mode has
 * been used, but it's difficult to search for. This was a compile-time setting
 * in the original, so setting this as constant. If this is exposed in the
 * public API, remove the ignore coverage directives on
 * libtess.normal.projectPolygon and libtess.normal.normalize_.
 * @const {boolean}
 */
libtess.TRUE_PROJECT = false;

/**
 * The default tolerance for merging features, 0, meaning vertices are only
 * merged if they are exactly coincident
 * If a higher tolerance is needed, significant rewriting will need to occur.
 * See libtess.sweep.TOLERANCE_NONZERO_ as a starting place.
 * @const {number}
 */
libtess.GLU_TESS_DEFAULT_TOLERANCE = 0;

/**
 * The input contours parition the plane into regions. A winding
 * rule determines which of these regions are inside the polygon.
 *
 * For a single contour C, the winding number of a point x is simply
 * the signed number of revolutions we make around x as we travel
 * once around C (where CCW is positive). When there are several
 * contours, the individual winding numbers are summed. This
 * procedure associates a signed integer value with each point x in
 * the plane. Note that the winding number is the same for all
 * points in a single region.
 *
 * The winding rule classifies a region as "inside" if its winding
 * number belongs to the chosen category (odd, nonzero, positive,
 * negative, or absolute value of at least two). The current GLU
 * tesselator implements the "odd" rule. The "nonzero" rule is another
 * common way to define the interior. The other three rules are
 * useful for polygon CSG operations.
 * @enum {number}
 */
libtess.windingRule = {
  // NOTE(bckenny): values from enumglu.spec
  GLU_TESS_WINDING_ODD: 100130,
  GLU_TESS_WINDING_NONZERO: 100131,
  GLU_TESS_WINDING_POSITIVE: 100132,
  GLU_TESS_WINDING_NEGATIVE: 100133,
  GLU_TESS_WINDING_ABS_GEQ_TWO: 100134
};

/**
 * The type of primitive return from a "begin" callback. GL_LINE_LOOP is only
 * returned when GLU_TESS_BOUNDARY_ONLY is true. GL_TRIANGLE_STRIP and
 * GL_TRIANGLE_FAN are no longer returned since 1.1.0 (see release notes).
 * @enum {number}
 */
libtess.primitiveType = {
  GL_LINE_LOOP: 2,
  GL_TRIANGLES: 4,
  GL_TRIANGLE_STRIP: 5,
  GL_TRIANGLE_FAN: 6
};

/**
 * The types of errors provided in the error callback.
 * @enum {number}
 */
libtess.errorType = {
  // TODO(bckenny) doc types
  // NOTE(bckenny): values from enumglu.spec
  GLU_TESS_MISSING_BEGIN_POLYGON: 100151,
  GLU_TESS_MISSING_END_POLYGON: 100153,
  GLU_TESS_MISSING_BEGIN_CONTOUR: 100152,
  GLU_TESS_MISSING_END_CONTOUR: 100154,
  GLU_TESS_COORD_TOO_LARGE: 100155,
  GLU_TESS_NEED_COMBINE_CALLBACK: 100156
};

/**
 * Enum values necessary for providing settings and callbacks. See the readme
 * for details.
 * @enum {number}
 */
libtess.gluEnum = {
  // TODO(bckenny): rename so not always typing libtess.gluEnum.*?

  // NOTE(bckenny): values from enumglu.spec
  GLU_TESS_BEGIN: 100100,
  GLU_TESS_VERTEX: 100101,
  GLU_TESS_END: 100102,
  GLU_TESS_ERROR: 100103,
  GLU_TESS_EDGE_FLAG: 100104,
  GLU_TESS_COMBINE: 100105,
  GLU_TESS_BEGIN_DATA: 100106,
  GLU_TESS_VERTEX_DATA: 100107,
  GLU_TESS_END_DATA: 100108,
  GLU_TESS_ERROR_DATA: 100109,
  GLU_TESS_EDGE_FLAG_DATA: 100110,
  GLU_TESS_COMBINE_DATA: 100111,

  GLU_TESS_MESH: 100112,  //  NOTE(bckenny): from tess.c
  GLU_TESS_TOLERANCE: 100142,
  GLU_TESS_WINDING_RULE: 100140,
  GLU_TESS_BOUNDARY_ONLY: 100141,

  // TODO(bckenny): move this to libtess.errorType?
  GLU_INVALID_ENUM: 100900,
  GLU_INVALID_VALUE: 100901
};

/** @typedef {number} */
libtess.PQHandle;

/* global libtess */

/** @const */
libtess.geom = {};

/**
 * Returns whether vertex u and vertex v are equal.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @return {boolean}
 */
libtess.geom.vertEq = function(u, v) {
  return u.s === v.s && u.t === v.t;
};

/**
 * Returns whether vertex u is lexicographically less than or equal to vertex v.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @return {boolean}
 */
libtess.geom.vertLeq = function(u, v) {
  return (u.s < v.s) || (u.s === v.s && u.t <= v.t);
};

/**
 * Given three vertices u,v,w such that geom.vertLeq(u,v) && geom.vertLeq(v,w),
 * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
 * Returns v.t - (uw)(v.s), ie. the signed distance from uw to v.
 * If uw is vertical (and thus passes thru v), the result is zero.
 *
 * The calculation is extremely accurate and stable, even when v
 * is very close to u or w.  In particular if we set v.t = 0 and
 * let r be the negated result (this evaluates (uw)(v.s)), then
 * r is guaranteed to satisfy MIN(u.t,w.t) <= r <= MAX(u.t,w.t).
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @param {libtess.GluVertex} w
 * @return {number}
 */
libtess.geom.edgeEval = function(u, v, w) {

  var gapL = v.s - u.s;
  var gapR = w.s - v.s;

  if (gapL + gapR > 0) {
    if (gapL < gapR) {
      return (v.t - u.t) + (u.t - w.t) * (gapL / (gapL + gapR));
    } else {
      return (v.t - w.t) + (w.t - u.t) * (gapR / (gapL + gapR));
    }
  }

  // vertical line
  return 0;
};

/**
 * Returns a number whose sign matches geom.edgeEval(u,v,w) but which
 * is cheaper to evaluate.  Returns > 0, == 0 , or < 0
 * as v is above, on, or below the edge uw.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @param {libtess.GluVertex} w
 * @return {number}
 */
libtess.geom.edgeSign = function(u, v, w) {

  var gapL = v.s - u.s;
  var gapR = w.s - v.s;

  if (gapL + gapR > 0) {
    return (v.t - w.t) * gapL + (v.t - u.t) * gapR;
  }

  // vertical line
  return 0;
};

/**
 * Version of VertLeq with s and t transposed.
 * Returns whether vertex u is lexicographically less than or equal to vertex v.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @return {boolean}
 */
libtess.geom.transLeq = function(u, v) {
  return (u.t < v.t) || (u.t === v.t && u.s <= v.s);
};

/**
 * Version of geom.edgeEval with s and t transposed.
 * Given three vertices u,v,w such that geom.transLeq(u,v) &&
 * geom.transLeq(v,w), evaluates the t-coord of the edge uw at the s-coord of
 * the vertex v. Returns v.s - (uw)(v.t), ie. the signed distance from uw to v.
 * If uw is vertical (and thus passes thru v), the result is zero.
 *
 * The calculation is extremely accurate and stable, even when v
 * is very close to u or w.  In particular if we set v.s = 0 and
 * let r be the negated result (this evaluates (uw)(v.t)), then
 * r is guaranteed to satisfy MIN(u.s,w.s) <= r <= MAX(u.s,w.s).
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @param {libtess.GluVertex} w
 * @return {number}
 */
libtess.geom.transEval = function(u, v, w) {

  var gapL = v.t - u.t;
  var gapR = w.t - v.t;

  if (gapL + gapR > 0) {
    if (gapL < gapR) {
      return (v.s - u.s) + (u.s - w.s) * (gapL / (gapL + gapR));
    } else {
      return (v.s - w.s) + (w.s - u.s) * (gapR / (gapL + gapR));
    }
  }

  // vertical line
  return 0;
};

/**
 * Version of geom.edgeSign with s and t transposed.
 * Returns a number whose sign matches geom.transEval(u,v,w) but which
 * is cheaper to evaluate.  Returns > 0, == 0 , or < 0
 * as v is above, on, or below the edge uw.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @param {libtess.GluVertex} w
 * @return {number}
 */
libtess.geom.transSign = function(u, v, w) {

  var gapL = v.t - u.t;
  var gapR = w.t - v.t;

  if (gapL + gapR > 0) {
    return (v.s - w.s) * gapL + (v.s - u.s) * gapR;
  }

  // vertical line
  return 0;
};

/**
 * Returns whether edge is directed from right to left.
 * @param {libtess.GluHalfEdge} e
 * @return {boolean}
 */
libtess.geom.edgeGoesLeft = function(e) {
  return libtess.geom.vertLeq(e.dst(), e.org);
};

/**
 * Returns whether edge is directed from left to right.
 * @param {libtess.GluHalfEdge} e
 * @return {boolean}
 */
libtess.geom.edgeGoesRight = function(e) {
  return libtess.geom.vertLeq(e.org, e.dst());
};

/**
 * Calculates the L1 distance between vertices u and v.
 * @param {libtess.GluVertex} u
 * @param {libtess.GluVertex} v
 * @return {number}
 */
libtess.geom.vertL1dist = function(u, v) {
  return Math.abs(u.s - v.s) + Math.abs(u.t - v.t);
};

// NOTE(bckenny): vertCCW is called nowhere in libtess and isn't part of the
// public API.
/* istanbul ignore next */
/**
 * For almost-degenerate situations, the results are not reliable.
 * Unless the floating-point arithmetic can be performed without
 * rounding errors, *any* implementation will give incorrect results
 * on some degenerate inputs, so the client must have some way to
 * handle this situation.
 * @param {!libtess.GluVertex} u
 * @param {!libtess.GluVertex} v
 * @param {!libtess.GluVertex} w
 * @return {boolean}
 */
libtess.geom.vertCCW = function(u, v, w) {
  return (u.s * (v.t - w.t) + v.s * (w.t - u.t) + w.s * (u.t - v.t)) >= 0;
};

/**
 * Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
 * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
 * this in the rare case that one argument is slightly negative.
 * The implementation is extremely stable numerically.
 * In particular it guarantees that the result r satisfies
 * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
 * even when a and b differ greatly in magnitude.
 * @private
 * @param {number} a
 * @param {number} x
 * @param {number} b
 * @param {number} y
 * @return {number}
 */
libtess.geom.interpolate_ = function(a, x, b, y) {
  // from Macro RealInterpolate:
  //(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, ((a <= b) ? ((b == 0) ? ((x+y) / 2) : (x + (y-x) * (a/(a+b)))) : (y + (x-y) * (b/(a+b)))))
  a = (a < 0) ? 0 : a;
  b = (b < 0) ? 0 : b;

  if (a <= b) {
    if (b === 0) {
      return (x + y) / 2;
    } else {
      return x + (y - x) * (a / (a + b));
    }
  } else {
    return y + (x - y) * (b / (a + b));
  }
};

/**
 * Given edges (o1,d1) and (o2,d2), compute their point of intersection.
 * The computed point is guaranteed to lie in the intersection of the
 * bounding rectangles defined by each edge.
 * @param {!libtess.GluVertex} o1
 * @param {!libtess.GluVertex} d1
 * @param {!libtess.GluVertex} o2
 * @param {!libtess.GluVertex} d2
 * @param {!libtess.GluVertex} v
 */
libtess.geom.edgeIntersect = function(o1, d1, o2, d2, v) {
  // This is certainly not the most efficient way to find the intersection
  // of two line segments, but it is very numerically stable.

  // Strategy: find the two middle vertices in the VertLeq ordering,
  // and interpolate the intersection s-value from these. Then repeat
  // using the TransLeq ordering to find the intersection t-value.
  var z1;
  var z2;
  var tmp;
  if (!libtess.geom.vertLeq(o1, d1)) {
    // Swap(o1, d1);
    tmp = o1;
    o1 = d1;
    d1 = tmp;
  }
  if (!libtess.geom.vertLeq(o2, d2)) {
    // Swap(o2, d2);
    tmp = o2;
    o2 = d2;
    d2 = tmp;
  }
  if (!libtess.geom.vertLeq(o1, o2)) {
    // Swap(o1, o2);
    tmp = o1;
    o1 = o2;
    o2 = tmp;
    // Swap(d1, d2);
    tmp = d1;
    d1 = d2;
    d2 = tmp;
  }

  if (!libtess.geom.vertLeq(o2, d1)) {
    // Technically, no intersection -- do our best
    v.s = (o2.s + d1.s) / 2;

  } else if (libtess.geom.vertLeq(d1, d2)) {
    // Interpolate between o2 and d1
    z1 = libtess.geom.edgeEval(o1, o2, d1);
    z2 = libtess.geom.edgeEval(o2, d1, d2);
    if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; }
    v.s = libtess.geom.interpolate_(z1, o2.s, z2, d1.s);

  } else {
    // Interpolate between o2 and d2
    z1 = libtess.geom.edgeSign(o1, o2, d1);
    z2 = -libtess.geom.edgeSign(o1, d2, d1);
    if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; }
    v.s = libtess.geom.interpolate_(z1, o2.s, z2, d2.s);
  }

  // Now repeat the process for t
  if (!libtess.geom.transLeq(o1, d1)) {
    // Swap(o1, d1);
    tmp = o1;
    o1 = d1;
    d1 = tmp;
  }
  if (!libtess.geom.transLeq(o2, d2)) {
    // Swap(o2, d2);
    tmp = o2;
    o2 = d2;
    d2 = tmp;
  }
  if (!libtess.geom.transLeq(o1, o2)) {
    // Swap(o1, o2);
    tmp = o1;
    o1 = o2;
    o2 = tmp;
    // Swap(d1, d2);
    tmp = d1;
    d1 = d2;
    d2 = tmp;
  }

  if (!libtess.geom.transLeq(o2, d1)) {
    // Technically, no intersection -- do our best
    v.t = (o2.t + d1.t) / 2;

  } else if (libtess.geom.transLeq(d1, d2)) {
    // Interpolate between o2 and d1
    z1 = libtess.geom.transEval(o1, o2, d1);
    z2 = libtess.geom.transEval(o2, d1, d2);
    if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; }
    v.t = libtess.geom.interpolate_(z1, o2.t, z2, d1.t);

  } else {
    // Interpolate between o2 and d2
    z1 = libtess.geom.transSign(o1, o2, d1);
    z2 = -libtess.geom.transSign(o1, d2, d1);
    if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; }
    v.t = libtess.geom.interpolate_(z1, o2.t, z2, d2.t);
  }
};

/* global libtess */

// TODO(bckenny): could maybe merge GluMesh and mesh.js since these are
// operations on the mesh

/** @const */
libtess.mesh = {};

/****************** Basic Edge Operations **********************/


/**
 * makeEdge creates one edge, two vertices, and a loop (face).
 * The loop consists of the two new half-edges.
 *
 * @param {libtess.GluMesh} mesh [description].
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.mesh.makeEdge = function(mesh) {
  // TODO(bckenny): probably move to GluMesh, but needs Make* methods with it

  var e = libtess.mesh.makeEdgePair_(mesh.eHead);

  // complete edge with vertices and face (see mesh.makeEdgePair_)
  libtess.mesh.makeVertex_(e, mesh.vHead);
  libtess.mesh.makeVertex_(e.sym, mesh.vHead);
  libtess.mesh.makeFace_(e, mesh.fHead);

  return e;
};


/**
 * meshSplice(eOrg, eDst) is the basic operation for changing the
 * mesh connectivity and topology. It changes the mesh so that
 *  eOrg.oNext <- OLD( eDst.oNext )
 *  eDst.oNext <- OLD( eOrg.oNext )
 * where OLD(...) means the value before the meshSplice operation.
 *
 * This can have two effects on the vertex structure:
 *  - if eOrg.org != eDst.org, the two vertices are merged together
 *  - if eOrg.org == eDst.org, the origin is split into two vertices
 * In both cases, eDst.org is changed and eOrg.org is untouched.
 *
 * Similarly (and independently) for the face structure,
 *  - if eOrg.lFace == eDst.lFace, one loop is split into two
 *  - if eOrg.lFace != eDst.lFace, two distinct loops are joined into one
 * In both cases, eDst.lFace is changed and eOrg.lFace is unaffected.
 *
 * Some special cases:
 * If eDst == eOrg, the operation has no effect.
 * If eDst == eOrg.lNext, the new face will have a single edge.
 * If eDst == eOrg.lPrev(), the old face will have a single edge.
 * If eDst == eOrg.oNext, the new vertex will have a single edge.
 * If eDst == eOrg.oPrev(), the old vertex will have a single edge.
 *
 * @param {libtess.GluHalfEdge} eOrg [description].
 * @param {libtess.GluHalfEdge} eDst [description].
 */
libtess.mesh.meshSplice = function(eOrg, eDst) {
  // TODO: more descriptive name?

  var joiningLoops = false;
  var joiningVertices = false;

  if (eOrg === eDst) {
    return;
  }

  if (eDst.org !== eOrg.org) {
    // We are merging two disjoint vertices -- destroy eDst.org
    joiningVertices = true;
    libtess.mesh.killVertex_(eDst.org, eOrg.org);
  }

  if (eDst.lFace !== eOrg.lFace) {
    // We are connecting two disjoint loops -- destroy eDst.lFace
    joiningLoops = true;
    libtess.mesh.killFace_(eDst.lFace, eOrg.lFace);
  }

  // Change the edge structure
  libtess.mesh.splice_(eDst, eOrg);

  if (!joiningVertices) {
    // We split one vertex into two -- the new vertex is eDst.org.
    // Make sure the old vertex points to a valid half-edge.
    libtess.mesh.makeVertex_(eDst, eOrg.org);
    eOrg.org.anEdge = eOrg;
  }

  if (!joiningLoops) {
    // We split one loop into two -- the new loop is eDst.lFace.
    // Make sure the old face points to a valid half-edge.
    libtess.mesh.makeFace_(eDst, eOrg.lFace);
    eOrg.lFace.anEdge = eOrg;
  }
};


/**
 * deleteEdge(eDel) removes the edge eDel. There are several cases:
 * if (eDel.lFace != eDel.rFace()), we join two loops into one; the loop
 * eDel.lFace is deleted. Otherwise, we are splitting one loop into two;
 * the newly created loop will contain eDel.dst(). If the deletion of eDel
 * would create isolated vertices, those are deleted as well.
 *
 * This function could be implemented as two calls to __gl_meshSplice
 * plus a few calls to memFree, but this would allocate and delete
 * unnecessary vertices and faces.
 *
 * @param {libtess.GluHalfEdge} eDel [description].
 */
libtess.mesh.deleteEdge = function(eDel) {
  var eDelSym = eDel.sym;
  var joiningLoops = false;

  // First step: disconnect the origin vertex eDel.org.  We make all
  // changes to get a consistent mesh in this "intermediate" state.
  if (eDel.lFace !== eDel.rFace()) {
    // We are joining two loops into one -- remove the left face
    joiningLoops = true;
    libtess.mesh.killFace_(eDel.lFace, eDel.rFace());
  }

  if (eDel.oNext === eDel) {
    libtess.mesh.killVertex_(eDel.org, null);

  } else {
    // Make sure that eDel.org and eDel.rFace() point to valid half-edges
    eDel.rFace().anEdge = eDel.oPrev();
    eDel.org.anEdge = eDel.oNext;

    libtess.mesh.splice_(eDel, eDel.oPrev());

    if (!joiningLoops) {
      // We are splitting one loop into two -- create a new loop for eDel.
      libtess.mesh.makeFace_(eDel, eDel.lFace);
    }
  }

  // Claim: the mesh is now in a consistent state, except that eDel.org
  // may have been deleted.  Now we disconnect eDel.dst().
  if (eDelSym.oNext === eDelSym) {
    libtess.mesh.killVertex_(eDelSym.org, null);
    libtess.mesh.killFace_(eDelSym.lFace, null);

  } else {
    // Make sure that eDel.dst() and eDel.lFace point to valid half-edges
    eDel.lFace.anEdge = eDelSym.oPrev();
    eDelSym.org.anEdge = eDelSym.oNext;
    libtess.mesh.splice_(eDelSym, eDelSym.oPrev());
  }

  // Any isolated vertices or faces have already been freed.
  libtess.mesh.killEdge_(eDel);
};

/******************** Other Edge Operations **********************/

/* All these routines can be implemented with the basic edge
 * operations above.  They are provided for convenience and efficiency.
 */


/**
 * addEdgeVertex(eOrg) creates a new edge eNew such that
 * eNew == eOrg.lNext, and eNew.dst() is a newly created vertex.
 * eOrg and eNew will have the same left face.
 *
 * @param {libtess.GluHalfEdge} eOrg [description].
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.mesh.addEdgeVertex = function(eOrg) {
  // TODO(bckenny): why is it named this?

  var eNew = libtess.mesh.makeEdgePair_(eOrg);
  var eNewSym = eNew.sym;

  // Connect the new edge appropriately
  libtess.mesh.splice_(eNew, eOrg.lNext);

  // Set the vertex and face information
  eNew.org = eOrg.dst();

  libtess.mesh.makeVertex_(eNewSym, eNew.org);

  eNew.lFace = eNewSym.lFace = eOrg.lFace;

  return eNew;
};


/**
 * splitEdge(eOrg) splits eOrg into two edges eOrg and eNew,
 * such that eNew == eOrg.lNext. The new vertex is eOrg.dst() == eNew.org.
 * eOrg and eNew will have the same left face.
 *
 * @param {libtess.GluHalfEdge} eOrg [description].
 * @return {!libtess.GluHalfEdge} [description].
 */
libtess.mesh.splitEdge = function(eOrg) {
  var tempHalfEdge = libtess.mesh.addEdgeVertex(eOrg);
  var eNew = tempHalfEdge.sym;

  // Disconnect eOrg from eOrg.dst() and connect it to eNew.org
  libtess.mesh.splice_(eOrg.sym, eOrg.sym.oPrev());
  libtess.mesh.splice_(eOrg.sym, eNew);

  // Set the vertex and face information
  eOrg.sym.org = eNew.org; // NOTE(bckenny): assignment to dst
  eNew.dst().anEdge = eNew.sym;  // may have pointed to eOrg.sym
  eNew.sym.lFace = eOrg.rFace(); // NOTE(bckenny): assignment to rFace
  eNew.winding = eOrg.winding;  // copy old winding information
  eNew.sym.winding = eOrg.sym.winding;

  return eNew;
};


/**
 * connect(eOrg, eDst) creates a new edge from eOrg.dst()
 * to eDst.org, and returns the corresponding half-edge eNew.
 * If eOrg.lFace == eDst.lFace, this splits one loop into two,
 * and the newly created loop is eNew.lFace. Otherwise, two disjoint
 * loops are merged into one, and the loop eDst.lFace is destroyed.
 *
 * If (eOrg == eDst), the new face will have only two edges.
 * If (eOrg.lNext == eDst), the old face is reduced to a single edge.
 * If (eOrg.lNext.lNext == eDst), the old face is reduced to two edges.
 *
 * @param {libtess.GluHalfEdge} eOrg [description].
 * @param {libtess.GluHalfEdge} eDst [description].
 * @return {!libtess.GluHalfEdge} [description].
 */
libtess.mesh.connect = function(eOrg, eDst) {
  var joiningLoops = false;
  var eNew = libtess.mesh.makeEdgePair_(eOrg);
  var eNewSym = eNew.sym;

  if (eDst.lFace !== eOrg.lFace) {
    // We are connecting two disjoint loops -- destroy eDst.lFace
    joiningLoops = true;
    libtess.mesh.killFace_(eDst.lFace, eOrg.lFace);
  }

  // Connect the new edge appropriately
  libtess.mesh.splice_(eNew, eOrg.lNext);
  libtess.mesh.splice_(eNewSym, eDst);

  // Set the vertex and face information
  eNew.org = eOrg.dst();
  eNewSym.org = eDst.org;
  eNew.lFace = eNewSym.lFace = eOrg.lFace;

  // Make sure the old face points to a valid half-edge
  eOrg.lFace.anEdge = eNewSym;

  if (!joiningLoops) {
    // We split one loop into two -- the new loop is eNew.lFace
    libtess.mesh.makeFace_(eNew, eOrg.lFace);
  }
  return eNew;
};

/******************** Other Operations **********************/


/**
 * zapFace(fZap) destroys a face and removes it from the
 * global face list. All edges of fZap will have a null pointer as their
 * left face. Any edges which also have a null pointer as their right face
 * are deleted entirely (along with any isolated vertices this produces).
 * An entire mesh can be deleted by zapping its faces, one at a time,
 * in any order. Zapped faces cannot be used in further mesh operations!
 *
 * @param {libtess.GluFace} fZap [description].
 */
libtess.mesh.zapFace = function(fZap) {
  var eStart = fZap.anEdge;

  // walk around face, deleting edges whose right face is also NULL
  var eNext = eStart.lNext;
  var e;
  do {
    e = eNext;
    eNext = e.lNext;

    e.lFace = null;
    if (e.rFace() === null) {
      // delete the edge -- see mesh.deleteEdge above
      if (e.oNext === e) {
        libtess.mesh.killVertex_(e.org, null);

      } else {
        // Make sure that e.org points to a valid half-edge
        e.org.anEdge = e.oNext;
        libtess.mesh.splice_(e, e.oPrev());
      }

      var eSym = e.sym;

      if (eSym.oNext === eSym) {
        libtess.mesh.killVertex_(eSym.org, null);

      } else {
        // Make sure that eSym.org points to a valid half-edge
        eSym.org.anEdge = eSym.oNext;
        libtess.mesh.splice_(eSym, eSym.oPrev());
      }
      libtess.mesh.killEdge_(e);
    }
  } while (e !== eStart);

  // delete from circular doubly-linked list
  var fPrev = fZap.prev;
  var fNext = fZap.next;
  fNext.prev = fPrev;
  fPrev.next = fNext;

  // TODO(bckenny): memFree( fZap );
  // TODO(bckenny): probably null at callsite
};

// TODO(bckenny): meshUnion isn't called within libtess and isn't part of the
// public API. Could be useful if more mesh manipulation functions are exposed.
/* istanbul ignore next */
/**
 * meshUnion() forms the union of all structures in
 * both meshes, and returns the new mesh (the old meshes are destroyed).
 *
 * @param {!libtess.GluMesh} mesh1
 * @param {!libtess.GluMesh} mesh2
 * @return {!libtess.GluMesh}
 */
libtess.mesh.meshUnion = function(mesh1, mesh2) {
  // TODO(bceknny): probably move to GluMesh method
  var f1 = mesh1.fHead;
  var v1 = mesh1.vHead;
  var e1 = mesh1.eHead;

  var f2 = mesh2.fHead;
  var v2 = mesh2.vHead;
  var e2 = mesh2.eHead;

  // Add the faces, vertices, and edges of mesh2 to those of mesh1
  if (f2.next !== f2) {
    f1.prev.next = f2.next;
    f2.next.prev = f1.prev;
    f2.prev.next = f1;
    f1.prev = f2.prev;
  }

  if (v2.next !== v2) {
    v1.prev.next = v2.next;
    v2.next.prev = v1.prev;
    v2.prev.next = v1;
    v1.prev = v2.prev;
  }

  if (e2.next !== e2) {
    e1.sym.next.sym.next = e2.next;
    e2.next.sym.next = e1.sym.next;
    e2.sym.next.sym.next = e1;
    e1.sym.next = e2.sym.next;
  }

  // TODO(bckenny): memFree(mesh2);
  // TODO(bckenny): If function is kept, remove mesh2's data to enforce.
  return mesh1;
};


/**
 * deleteMesh(mesh) will free all storage for any valid mesh.
 * @param {libtess.GluMesh} mesh [description].
 */
libtess.mesh.deleteMesh = function(mesh) {
  // TODO(bckenny): unnecessary, I think.
  // TODO(bckenny): might want to explicitly null at callsite
  // lots of memFrees. see also DELETE_BY_ZAPPING
};

/************************ Utility Routines ************************/


/**
 * Creates a new pair of half-edges which form their own loop.
 * No vertex or face structures are allocated, but these must be assigned
 * before the current edge operation is completed.
 *
 * TODO(bckenny): warning about eNext strictly being first of pair? (see code)
 *
 * @private
 * @param {libtess.GluHalfEdge} eNext [description].
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.mesh.makeEdgePair_ = function(eNext) {
  var e = new libtess.GluHalfEdge();
  var eSym = new libtess.GluHalfEdge();

  // TODO(bckenny): how do we ensure this? see above comment in jsdoc
  // Make sure eNext points to the first edge of the edge pair
  // if (eNext->Sym < eNext ) { eNext = eNext->Sym; }

  // NOTE(bckenny): check this for bugs in current implementation!

  // Insert in circular doubly-linked list before eNext.
  // Note that the prev pointer is stored in sym.next.
  var ePrev = eNext.sym.next;
  eSym.next = ePrev;
  ePrev.sym.next = e;
  e.next = eNext;
  eNext.sym.next = eSym;

  e.sym = eSym;
  e.oNext = e;
  e.lNext = eSym;

  eSym.sym = e;
  eSym.oNext = eSym;
  eSym.lNext = e;

  return e;
};


/**
 * splice_ is best described by the Guibas/Stolfi paper or the
 * CS348a notes. Basically, it modifies the mesh so that
 * a.oNext and b.oNext are exchanged. This can have various effects
 * depending on whether a and b belong to different face or vertex rings.
 * For more explanation see mesh.meshSplice below.
 *
 * @private
 * @param {libtess.GluHalfEdge} a [description].
 * @param {libtess.GluHalfEdge} b [description].
 */
libtess.mesh.splice_ = function(a, b) {
  var aONext = a.oNext;
  var bONext = b.oNext;

  aONext.sym.lNext = b;
  bONext.sym.lNext = a;
  a.oNext = bONext;
  b.oNext = aONext;
};


/**
 * makeVertex_(eOrig, vNext) attaches a new vertex and makes it the
 * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
 * a place to insert the new vertex in the global vertex list.  We insert
 * the new vertex *before* vNext so that algorithms which walk the vertex
 * list will not see the newly created vertices.
 *
 * NOTE: unlike original, acutally allocates new vertex.
 *
 * @private
 * @param {libtess.GluHalfEdge} eOrig [description].
 * @param {libtess.GluVertex} vNext [description].
 */
libtess.mesh.makeVertex_ = function(eOrig, vNext) {
  // insert in circular doubly-linked list before vNext
  var vPrev = vNext.prev;
  var vNew = new libtess.GluVertex(vNext, vPrev);
  vPrev.next = vNew;
  vNext.prev = vNew;

  vNew.anEdge = eOrig;
  // leave coords, s, t undefined
  // TODO(bckenny): does above line mean 0 specifically, or does it matter?

  // fix other edges on this vertex loop
  var e = eOrig;
  do {
    e.org = vNew;
    e = e.oNext;
  } while (e !== eOrig);
};


/**
 * makeFace_(eOrig, fNext) attaches a new face and makes it the left
 * face of all edges in the face loop to which eOrig belongs. "fNext" gives
 * a place to insert the new face in the global face list.  We insert
 * the new face *before* fNext so that algorithms which walk the face
 * list will not see the newly created faces.
 *
 * NOTE: unlike original, acutally allocates new face.
 *
 * @private
 * @param {libtess.GluHalfEdge} eOrig [description].
 * @param {libtess.GluFace} fNext [description].
 */
libtess.mesh.makeFace_ = function(eOrig, fNext) {
  // insert in circular doubly-linked list before fNext
  var fPrev = fNext.prev;
  var fNew = new libtess.GluFace(fNext, fPrev);
  fPrev.next = fNew;
  fNext.prev = fNew;

  fNew.anEdge = eOrig;

  // The new face is marked "inside" if the old one was.  This is a
  // convenience for the common case where a face has been split in two.
  fNew.inside = fNext.inside;

  // fix other edges on this face loop
  var e = eOrig;
  do {
    e.lFace = fNew;
    e = e.lNext;
  } while (e !== eOrig);
};


/**
 * killEdge_ destroys an edge (the half-edges eDel and eDel.sym),
 * and removes from the global edge list.
 *
 * @private
 * @param {libtess.GluHalfEdge} eDel [description].
 */
libtess.mesh.killEdge_ = function(eDel) {
  // TODO(bckenny): in this case, no need to worry(?), but check when checking mesh.makeEdgePair_
  // Half-edges are allocated in pairs, see EdgePair above
  // if (eDel->Sym < eDel ) { eDel = eDel->Sym; }

  // delete from circular doubly-linked list
  var eNext = eDel.next;
  var ePrev = eDel.sym.next;
  eNext.sym.next = ePrev;
  ePrev.sym.next = eNext;

  // TODO(bckenny): memFree( eDel ); (which also frees eDel.sym)
  // TODO(bckenny): need to null at callsites?
};


/**
 * killVertex_ destroys a vertex and removes it from the global
 * vertex list. It updates the vertex loop to point to a given new vertex.
 *
 * @private
 * @param {libtess.GluVertex} vDel [description].
 * @param {libtess.GluVertex} newOrg [description].
 */
libtess.mesh.killVertex_ = function(vDel, newOrg) {
  var eStart = vDel.anEdge;

  // change the origin of all affected edges
  var e = eStart;
  do {
    e.org = newOrg;
    e = e.oNext;
  } while (e !== eStart);

  // delete from circular doubly-linked list
  var vPrev = vDel.prev;
  var vNext = vDel.next;
  vNext.prev = vPrev;
  vPrev.next = vNext;

  // TODO(bckenny): memFree( vDel );
  // TODO(bckenny): need to null at callsites?
};


/**
 * killFace_ destroys a face and removes it from the global face
 * list. It updates the face loop to point to a given new face.
 *
 * @private
 * @param {libtess.GluFace} fDel [description].
 * @param {libtess.GluFace} newLFace [description].
 */
libtess.mesh.killFace_ = function(fDel, newLFace) {
  var eStart = fDel.anEdge;

  // change the left face of all affected edges
  var e = eStart;
  do {
    e.lFace = newLFace;
    e = e.lNext;
  } while (e !== eStart);

  // delete from circular doubly-linked list
  var fPrev = fDel.prev;
  var fNext = fDel.next;
  fNext.prev = fPrev;
  fPrev.next = fNext;

  // TODO(bckenny): memFree( fDel );
  // TODO(bckenny): need to null at callsites?
};

/* global libtess */

/** @const */
libtess.normal = {};

// TODO(bckenny): Integrate SLANTED_SWEEP somehow?
/* The "feature merging" is not intended to be complete. There are
 * special cases where edges are nearly parallel to the sweep line
 * which are not implemented. The algorithm should still behave
 * robustly (ie. produce a reasonable tesselation) in the presence
 * of such edges, however it may miss features which could have been
 * merged. We could minimize this effect by choosing the sweep line
 * direction to be something unusual (ie. not parallel to one of the
 * coordinate axes).
 * #if defined(SLANTED_SWEEP)
 * #define S_UNIT_X  0.50941539564955385 // Pre-normalized
 * #define S_UNIT_Y  0.86052074622010633
 * #endif
 */

/**
 * X coordinate of local basis for polygon projection.
 * @private {number}
 * @const
 */
libtess.normal.S_UNIT_X_ = 1.0;

/**
 * Y coordinate of local basis for polygon projection.
 * @private {number}
 * @const
 */
libtess.normal.S_UNIT_Y_ = 0.0;

/**
 * Determines a polygon normal and projects vertices onto the plane of the
 * polygon. A pre-computed normal for the data may be provided, or set to the
 * zero vector if one should be computed from it.
 * @param {!libtess.GluTesselator} tess
 * @param {number} normalX
 * @param {number} normalY
 * @param {number} normalZ
 */
libtess.normal.projectPolygon = function(tess, normalX, normalY, normalZ) {
  var computedNormal = false;

  var norm = [
    normalX,
    normalY,
    normalZ
  ];
  if (normalX === 0 && normalY === 0 && normalZ === 0) {
    libtess.normal.computeNormal_(tess, norm);
    computedNormal = true;
  }

  var i = libtess.normal.longAxis_(norm);
  var vHead = tess.mesh.vHead;
  var v;

  // NOTE(bckenny): This branch is never taken. See comment on
  // libtess.TRUE_PROJECT.
  /* istanbul ignore if */
  if (libtess.TRUE_PROJECT) {
    // Choose the initial sUnit vector to be approximately perpendicular
    // to the normal.
    libtess.normal.normalize_(norm);

    var sUnit = [0, 0, 0];
    var tUnit = [0, 0, 0];

    sUnit[i] = 0;
    sUnit[(i + 1) % 3] = libtess.normal.S_UNIT_X_;
    sUnit[(i + 2) % 3] = libtess.normal.S_UNIT_Y_;

    // Now make it exactly perpendicular
    var w = libtess.normal.dot_(sUnit, norm);
    sUnit[0] -= w * norm[0];
    sUnit[1] -= w * norm[1];
    sUnit[2] -= w * norm[2];
    libtess.normal.normalize_(sUnit);

    // Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame
    tUnit[0] = norm[1] * sUnit[2] - norm[2] * sUnit[1];
    tUnit[1] = norm[2] * sUnit[0] - norm[0] * sUnit[2];
    tUnit[2] = norm[0] * sUnit[1] - norm[1] * sUnit[0];
    libtess.normal.normalize_(tUnit);

    // Project the vertices onto the sweep plane
    for (v = vHead.next; v !== vHead; v = v.next) {
      v.s = libtess.normal.dot_(v.coords, sUnit);
      v.t = libtess.normal.dot_(v.coords, tUnit);
    }

  } else {
    // Project perpendicular to a coordinate axis -- better numerically
    var sAxis = (i + 1) % 3;
    var tAxis = (i + 2) % 3;
    var tNegate = norm[i] > 0 ? 1 : -1;

    // Project the vertices onto the sweep plane
    for (v = vHead.next; v !== vHead; v = v.next) {
      v.s = v.coords[sAxis];
      v.t = tNegate * v.coords[tAxis];
    }
  }

  if (computedNormal) {
    libtess.normal.checkOrientation_(tess);
  }
};

// NOTE(bckenny): libtess.normal.dot_ is no longer called in code without
// libtess.TRUE_PROJECT defined.
/* istanbul ignore next */
/**
 * Computes the dot product of vectors u and v.
 * @private
 * @param {!Array<number>} u
 * @param {!Array<number>} v
 * @return {number}
 */
libtess.normal.dot_ = function(u, v) {
  return u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
};

// NOTE(bckenny): only called from within libtess.normal.projectPolygon's
// TRUE_PROJECT branch, so ignoring for code coverage.
/* istanbul ignore next */
/**
 * Normalize vector v.
 * @private
 * @param {!Array<number>} v
 */
libtess.normal.normalize_ = function(v) {
  var len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];

  len = Math.sqrt(len);
  v[0] /= len;
  v[1] /= len;
  v[2] /= len;
};

/**
 * Returns the index of the longest component of vector v.
 * @private
 * @param {!Array<number>} v
 * @return {number}
 */
libtess.normal.longAxis_ = function(v) {
  var i = 0;

  if (Math.abs(v[1]) > Math.abs(v[0])) {
    i = 1;
  }
  if (Math.abs(v[2]) > Math.abs(v[i])) {
    i = 2;
  }

  return i;
};

/**
 * Compute an approximate normal of the polygon from the vertices themselves.
 * Result returned in norm.
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {!Array<number>} norm
 */
libtess.normal.computeNormal_ = function(tess, norm) {
  var maxVal = [
    -2 * libtess.GLU_TESS_MAX_COORD,
    -2 * libtess.GLU_TESS_MAX_COORD,
    -2 * libtess.GLU_TESS_MAX_COORD
  ];
  var minVal = [
    2 * libtess.GLU_TESS_MAX_COORD,
    2 * libtess.GLU_TESS_MAX_COORD,
    2 * libtess.GLU_TESS_MAX_COORD
  ];
  var maxVert = [];
  var minVert = [];

  var v;
  var vHead = tess.mesh.vHead;
  for (v = vHead.next; v !== vHead; v = v.next) {
    for (var i = 0; i < 3; ++i) {
      var c = v.coords[i];
      if (c < minVal[i]) { minVal[i] = c; minVert[i] = v; }
      if (c > maxVal[i]) { maxVal[i] = c; maxVert[i] = v; }
    }
  }

  // Find two vertices separated by at least 1/sqrt(3) of the maximum
  // distance between any two vertices
  var index = 0;
  if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) { index = 1; }
  if (maxVal[2] - minVal[2] > maxVal[index] - minVal[index]) { index = 2; }
  if (minVal[index] >= maxVal[index]) {
    // All vertices are the same -- normal doesn't matter
    norm[0] = 0; norm[1] = 0; norm[2] = 1;
    return;
  }

  // Look for a third vertex which forms the triangle with maximum area
  // (Length of normal == twice the triangle area)
  var maxLen2 = 0;
  var v1 = minVert[index];
  var v2 = maxVert[index];
  var tNorm = [0, 0, 0];
  var d1 = [
    v1.coords[0] - v2.coords[0],
    v1.coords[1] - v2.coords[1],
    v1.coords[2] - v2.coords[2]
  ];
  var d2 = [0, 0, 0];
  for (v = vHead.next; v !== vHead; v = v.next) {
    d2[0] = v.coords[0] - v2.coords[0];
    d2[1] = v.coords[1] - v2.coords[1];
    d2[2] = v.coords[2] - v2.coords[2];
    tNorm[0] = d1[1] * d2[2] - d1[2] * d2[1];
    tNorm[1] = d1[2] * d2[0] - d1[0] * d2[2];
    tNorm[2] = d1[0] * d2[1] - d1[1] * d2[0];
    var tLen2 = tNorm[0] * tNorm[0] + tNorm[1] * tNorm[1] + tNorm[2] * tNorm[2];
    if (tLen2 > maxLen2) {
      maxLen2 = tLen2;
      norm[0] = tNorm[0];
      norm[1] = tNorm[1];
      norm[2] = tNorm[2];
    }
  }

  if (maxLen2 <= 0) {
    // All points lie on a single line -- any decent normal will do
    norm[0] = norm[1] = norm[2] = 0;
    norm[libtess.normal.longAxis_(d1)] = 1;
  }
};

/**
 * Check that the sum of the signed area of all projected contours is
 * non-negative. If not, negate the t-coordinates to reverse the orientation and
 * make it so.
 * @private
 * @param {!libtess.GluTesselator} tess
 */
libtess.normal.checkOrientation_ = function(tess) {
  var area = 0;
  var fHead = tess.mesh.fHead;
  for (var f = fHead.next; f !== fHead; f = f.next) {
    var e = f.anEdge;
    if (e.winding <= 0) { continue; }
    do {
      area += (e.org.s - e.dst().s) * (e.org.t + e.dst().t);
      e = e.lNext;
    } while (e !== f.anEdge);
  }

  if (area < 0) {
    // Reverse the orientation by flipping all the t-coordinates
    var vHead = tess.mesh.vHead;
    for (var v = vHead.next; v !== vHead; v = v.next) {
      v.t = -v.t;
    }
  }
};

/* global libtess */

/** @const */
libtess.render = {};

/**
 * Takes a mesh, breaks it into separate triangles, and renders them. The
 * rendering output is provided as callbacks (see the API). Set flagEdges to
 * true to get edgeFlag callbacks (tess.flagBoundary in original libtess).
 * @param {!libtess.GluTesselator} tess
 * @param {!libtess.GluMesh} mesh
 * @param {boolean} flagEdges
 */
libtess.render.renderMesh = function(tess, mesh, flagEdges) {
  var beginCallbackCalled = false;

  // TODO(bckenny): edgeState needs to be boolean, but !== on first call
  // force edge state output for first vertex
  var edgeState = -1;

  // We examine all faces in an arbitrary order. Whenever we find
  // an inside triangle f, we render f.
  // NOTE(bckenny): go backwards through face list to match original libtess
  // triangle order
  for (var f = mesh.fHead.prev; f !== mesh.fHead; f = f.prev) {
    if (f.inside) {
      // We're going to emit a triangle, so call begin callback once
      if (!beginCallbackCalled) {
        tess.callBeginCallback(libtess.primitiveType.GL_TRIANGLES);
        beginCallbackCalled = true;
      }

      // check that face has only three edges
      var e = f.anEdge;
      // Loop once for each edge (there will always be 3 edges)
      do {
        if (flagEdges) {
          // Set the "edge state" to true just before we output the
          // first vertex of each edge on the polygon boundary.
          var newState = !e.rFace().inside ? 1 : 0; // TODO(bckenny): total hack to get edgeState working. fix me.
          if (edgeState !== newState) {
            edgeState = newState;
            // TODO(bckenny): edgeState should be boolean now
            tess.callEdgeFlagCallback(!!edgeState);
          }
        }

        // emit vertex
        tess.callVertexCallback(e.org.data);

        e = e.lNext;
      } while (e !== f.anEdge);
    }
  }

  // only call end callback if begin was called
  if (beginCallbackCalled) {
    tess.callEndCallback();
  }
};

/**
 * Takes a mesh, and outputs one contour for each face marked "inside". The
 * rendering output is provided as callbacks (see the API).
 * @param {!libtess.GluTesselator} tess
 * @param {!libtess.GluMesh} mesh
 */
libtess.render.renderBoundary = function(tess, mesh) {
  for (var f = mesh.fHead.next; f !== mesh.fHead; f = f.next) {
    if (f.inside) {
      tess.callBeginCallback(libtess.primitiveType.GL_LINE_LOOP);

      var e = f.anEdge;
      do {
        tess.callVertexCallback(e.org.data);
        e = e.lNext;
      } while (e !== f.anEdge);

      tess.callEndCallback();
    }
  }
};

/* global libtess */

// TODO(bckenny): a number of these never return null (as opposed to original) and should be typed appropriately

/*
 * Invariants for the Edge Dictionary.
 * - each pair of adjacent edges e2=succ(e1) satisfies edgeLeq_(e1,e2)
 *   at any valid location of the sweep event
 * - if edgeLeq_(e2,e1) as well (at any valid sweep event), then e1 and e2
 *   share a common endpoint
 * - for each e, e.dst() has been processed, but not e.org
 * - each edge e satisfies vertLeq(e.dst(),event) && vertLeq(event,e.org)
 *   where "event" is the current sweep line event.
 * - no edge e has zero length
 *
 * Invariants for the Mesh (the processed portion).
 * - the portion of the mesh left of the sweep line is a planar graph,
 *   ie. there is *some* way to embed it in the plane
 * - no processed edge has zero length
 * - no two processed vertices have identical coordinates
 * - each "inside" region is monotone, ie. can be broken into two chains
 *   of monotonically increasing vertices according to VertLeq(v1,v2)
 *   - a non-invariant: these chains may intersect (very slightly)
 *
 * Invariants for the Sweep.
 * - if none of the edges incident to the event vertex have an activeRegion
 *   (ie. none of these edges are in the edge dictionary), then the vertex
 *   has only right-going edges.
 * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
 *   by ConnectRightVertex), then it is the only right-going edge from
 *   its associated vertex.  (This says that these edges exist only
 *   when it is necessary.)
 */

/** @const */
libtess.sweep = {};


/**
 * Make the sentinel coordinates big enough that they will never be
 * merged with real input features.  (Even with the largest possible
 * input contour and the maximum tolerance of 1.0, no merging will be
 * done with coordinates larger than 3 * libtess.GLU_TESS_MAX_COORD).
 * @private
 * @const
 * @type {number}
 */
libtess.sweep.SENTINEL_COORD_ = 4 * libtess.GLU_TESS_MAX_COORD;


/**
 * Because vertices at exactly the same location are merged together
 * before we process the sweep event, some degenerate cases can't occur.
 * However if someone eventually makes the modifications required to
 * merge features which are close together, the cases below marked
 * TOLERANCE_NONZERO will be useful.  They were debugged before the
 * code to merge identical vertices in the main loop was added.
 * @private
 * @const
 * @type {boolean}
 */
libtess.sweep.TOLERANCE_NONZERO_ = false;


/**
 * computeInterior(tess) computes the planar arrangement specified
 * by the given contours, and further subdivides this arrangement
 * into regions. Each region is marked "inside" if it belongs
 * to the polygon, according to the rule given by tess.windingRule.
 * Each interior region is guaranteed be monotone.
 *
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.computeInterior = function(tess) {
  tess.fatalError = false;

  // Each vertex defines an event for our sweep line. Start by inserting
  // all the vertices in a priority queue. Events are processed in
  // lexicographic order, ie.
  // e1 < e2  iff  e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)
  libtess.sweep.removeDegenerateEdges_(tess);
  libtess.sweep.initPriorityQ_(tess);
  libtess.sweep.initEdgeDict_(tess);

  var v;
  while ((v = tess.pq.extractMin()) !== null) {
    for (;;) {
      var vNext = tess.pq.minimum();
      if (vNext === null || !libtess.geom.vertEq(vNext, v)) {
        break;
      }

      /* Merge together all vertices at exactly the same location.
       * This is more efficient than processing them one at a time,
       * simplifies the code (see connectLeftDegenerate), and is also
       * important for correct handling of certain degenerate cases.
       * For example, suppose there are two identical edges A and B
       * that belong to different contours (so without this code they would
       * be processed by separate sweep events).  Suppose another edge C
       * crosses A and B from above.  When A is processed, we split it
       * at its intersection point with C.  However this also splits C,
       * so when we insert B we may compute a slightly different
       * intersection point.  This might leave two edges with a small
       * gap between them.  This kind of error is especially obvious
       * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY).
       */
      vNext = tess.pq.extractMin();
      libtess.sweep.spliceMergeVertices_(tess, v.anEdge, vNext.anEdge);
    }
    libtess.sweep.sweepEvent_(tess, v);
  }

  // TODO(bckenny): what does the next comment mean? can we eliminate event except when debugging?
  // Set tess.event for debugging purposes
  var minRegion = tess.dict.getMin().getKey();
  tess.event = minRegion.eUp.org;
  libtess.sweep.doneEdgeDict_(tess);
  libtess.sweep.donePriorityQ_(tess);

  libtess.sweep.removeDegenerateFaces_(tess.mesh);
  tess.mesh.checkMesh();
};


/**
 * When we merge two edges into one, we need to compute the combined
 * winding of the new edge.
 * @private
 * @param {libtess.GluHalfEdge} eDst [description].
 * @param {libtess.GluHalfEdge} eSrc [description].
 */
libtess.sweep.addWinding_ = function(eDst, eSrc) {
  // NOTE(bckenny): from AddWinding macro
  eDst.winding += eSrc.winding;
  eDst.sym.winding += eSrc.sym.winding;
};


/**
 * Both edges must be directed from right to left (this is the canonical
 * direction for the upper edge of each region).
 *
 * The strategy is to evaluate a "t" value for each edge at the
 * current sweep line position, given by tess.event.  The calculations
 * are designed to be very stable, but of course they are not perfect.
 *
 * Special case: if both edge destinations are at the sweep event,
 * we sort the edges by slope (they would otherwise compare equally).
 *
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {!libtess.ActiveRegion} reg1
 * @param {!libtess.ActiveRegion} reg2
 * @return {boolean}
 */
libtess.sweep.edgeLeq_ = function(tess, reg1, reg2) {
  var event = tess.event;
  var e1 = reg1.eUp;
  var e2 = reg2.eUp;

  if (e1.dst() === event) {
    if (e2.dst() === event) {
      // Two edges right of the sweep line which meet at the sweep event.
      // Sort them by slope.
      if (libtess.geom.vertLeq(e1.org, e2.org)) {
        return libtess.geom.edgeSign(e2.dst(), e1.org, e2.org) <= 0;
      }

      return libtess.geom.edgeSign(e1.dst(), e2.org, e1.org) >= 0;
    }

    return libtess.geom.edgeSign(e2.dst(), event, e2.org) <= 0;
  }

  if (e2.dst() === event) {
    return libtess.geom.edgeSign(e1.dst(), event, e1.org) >= 0;
  }

  // General case - compute signed distance *from* e1, e2 to event
  var t1 = libtess.geom.edgeEval(e1.dst(), event, e1.org);
  var t2 = libtess.geom.edgeEval(e2.dst(), event, e2.org);
  return (t1 >= t2);
};


/**
 * [deleteRegion_ description]
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} reg [description].
 */
libtess.sweep.deleteRegion_ = function(tess, reg) {
  if (reg.fixUpperEdge) {
    // It was created with zero winding number, so it better be
    // deleted with zero winding number (ie. it better not get merged
    // with a real edge).
  }

  reg.eUp.activeRegion = null;

  tess.dict.deleteNode(reg.nodeUp);
  reg.nodeUp = null;

  // memFree( reg ); TODO(bckenny)
  // TODO(bckenny): may need to null at callsite
};


/**
 * Replace an upper edge which needs fixing (see connectRightVertex).
 * @private
 * @param {libtess.ActiveRegion} reg [description].
 * @param {libtess.GluHalfEdge} newEdge [description].
 */
libtess.sweep.fixUpperEdge_ = function(reg, newEdge) {
  libtess.mesh.deleteEdge(reg.eUp);

  reg.fixUpperEdge = false;
  reg.eUp = newEdge;
  newEdge.activeRegion = reg;
};


/**
 * Find the region above the uppermost edge with the same origin.
 * @private
 * @param {libtess.ActiveRegion} reg [description].
 * @return {libtess.ActiveRegion} [description].
 */
libtess.sweep.topLeftRegion_ = function(reg) {
  var org = reg.eUp.org;

  // Find the region above the uppermost edge with the same origin
  do {
    reg = reg.regionAbove();
  } while (reg.eUp.org === org);

  // If the edge above was a temporary edge introduced by connectRightVertex,
  // now is the time to fix it.
  if (reg.fixUpperEdge) {
    var e = libtess.mesh.connect(reg.regionBelow().eUp.sym, reg.eUp.lNext);
    libtess.sweep.fixUpperEdge_(reg, e);
    reg = reg.regionAbove();
  }

  return reg;
};


/**
 * Find the region above the uppermost edge with the same destination.
 * @private
 * @param {libtess.ActiveRegion} reg [description].
 * @return {libtess.ActiveRegion} [description].
 */
libtess.sweep.topRightRegion_ = function(reg) {
  var dst = reg.eUp.dst();

  do {
    reg = reg.regionAbove();
  } while (reg.eUp.dst() === dst);

  return reg;
};


/**
 * Add a new active region to the sweep line, *somewhere* below "regAbove"
 * (according to where the new edge belongs in the sweep-line dictionary).
 * The upper edge of the new region will be "eNewUp".
 * Winding number and "inside" flag are not updated.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regAbove [description].
 * @param {libtess.GluHalfEdge} eNewUp [description].
 * @return {libtess.ActiveRegion} regNew.
 */
libtess.sweep.addRegionBelow_ = function(tess, regAbove, eNewUp) {
  var regNew = new libtess.ActiveRegion();

  regNew.eUp = eNewUp;
  regNew.nodeUp = tess.dict.insertBefore(regAbove.nodeUp, regNew);
  eNewUp.activeRegion = regNew;

  return regNew;
};


/**
 * [isWindingInside_ description]
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {number} n int.
 * @return {boolean} [description].
 */
libtess.sweep.isWindingInside_ = function(tess, n) {
  switch (tess.windingRule) {
    case libtess.windingRule.GLU_TESS_WINDING_ODD:
      return ((n & 1) !== 0);
    case libtess.windingRule.GLU_TESS_WINDING_NONZERO:
      return (n !== 0);
    case libtess.windingRule.GLU_TESS_WINDING_POSITIVE:
      return (n > 0);
    case libtess.windingRule.GLU_TESS_WINDING_NEGATIVE:
      return (n < 0);
    case libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO:
      return (n >= 2) || (n <= -2);
  }

  // TODO(bckenny): not reached
  return false;
};


/**
 * [computeWinding_ description]
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} reg [description].
 */
libtess.sweep.computeWinding_ = function(tess, reg) {
  reg.windingNumber = reg.regionAbove().windingNumber + reg.eUp.winding;
  reg.inside = libtess.sweep.isWindingInside_(tess, reg.windingNumber);
};


/**
 * Delete a region from the sweep line. This happens when the upper
 * and lower chains of a region meet (at a vertex on the sweep line).
 * The "inside" flag is copied to the appropriate mesh face (we could
 * not do this before -- since the structure of the mesh is always
 * changing, this face may not have even existed until now).
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} reg [description].
 */
libtess.sweep.finishRegion_ = function(tess, reg) {
  // TODO(bckenny): may need to null reg at callsite

  var e = reg.eUp;
  var f = e.lFace;

  f.inside = reg.inside;
  f.anEdge = e;   // optimization for tessmono.tessellateMonoRegion() // TODO(bckenny): how so?
  libtess.sweep.deleteRegion_(tess, reg);
};


/**
 * We are given a vertex with one or more left-going edges. All affected
 * edges should be in the edge dictionary. Starting at regFirst.eUp,
 * we walk down deleting all regions where both edges have the same
 * origin vOrg. At the same time we copy the "inside" flag from the
 * active region to the face, since at this point each face will belong
 * to at most one region (this was not necessarily true until this point
 * in the sweep). The walk stops at the region above regLast; if regLast
 * is null we walk as far as possible. At the same time we relink the
 * mesh if necessary, so that the ordering of edges around vOrg is the
 * same as in the dictionary.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regFirst [description].
 * @param {libtess.ActiveRegion} regLast [description].
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.sweep.finishLeftRegions_ = function(tess, regFirst, regLast) {
  var regPrev = regFirst;
  var ePrev = regFirst.eUp;
  while (regPrev !== regLast) {
    // placement was OK
    regPrev.fixUpperEdge = false;
    var reg = regPrev.regionBelow();
    var e = reg.eUp;
    if (e.org !== ePrev.org) {
      if (!reg.fixUpperEdge) {
        /* Remove the last left-going edge. Even though there are no further
         * edges in the dictionary with this origin, there may be further
         * such edges in the mesh (if we are adding left edges to a vertex
         * that has already been processed). Thus it is important to call
         * finishRegion rather than just deleteRegion.
         */
        libtess.sweep.finishRegion_(tess, regPrev);
        break;
      }

      // If the edge below was a temporary edge introduced by
      // connectRightVertex, now is the time to fix it.
      e = libtess.mesh.connect(ePrev.lPrev(), e.sym);
      libtess.sweep.fixUpperEdge_(reg, e);
    }

    // Relink edges so that ePrev.oNext === e
    if (ePrev.oNext !== e) {
      libtess.mesh.meshSplice(e.oPrev(), e);
      libtess.mesh.meshSplice(ePrev, e);
    }

    // may change reg.eUp
    libtess.sweep.finishRegion_(tess, regPrev);
    ePrev = reg.eUp;
    regPrev = reg;
  }

  return ePrev;
};


/**
 * Purpose: insert right-going edges into the edge dictionary, and update
 * winding numbers and mesh connectivity appropriately. All right-going
 * edges share a common origin vOrg. Edges are inserted CCW starting at
 * eFirst; the last edge inserted is eLast.oPrev. If vOrg has any
 * left-going edges already processed, then eTopLeft must be the edge
 * such that an imaginary upward vertical segment from vOrg would be
 * contained between eTopLeft.oPrev and eTopLeft; otherwise eTopLeft
 * should be null.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regUp [description].
 * @param {libtess.GluHalfEdge} eFirst [description].
 * @param {libtess.GluHalfEdge} eLast [description].
 * @param {libtess.GluHalfEdge} eTopLeft [description].
 * @param {boolean} cleanUp [description].
 */
libtess.sweep.addRightEdges_ = function(tess, regUp, eFirst, eLast, eTopLeft,
    cleanUp) {

  var firstTime = true;

  // Insert the new right-going edges in the dictionary
  var e = eFirst;
  do {
    libtess.sweep.addRegionBelow_(tess, regUp, e.sym);
    e = e.oNext;
  } while (e !== eLast);

  // Walk *all* right-going edges from e.org, in the dictionary order,
  // updating the winding numbers of each region, and re-linking the mesh
  // edges to match the dictionary ordering (if necessary).
  if (eTopLeft === null) {
    eTopLeft = regUp.regionBelow().eUp.rPrev();
  }
  var regPrev = regUp;
  var ePrev = eTopLeft;
  var reg;
  for (;;) {
    reg = regPrev.regionBelow();
    e = reg.eUp.sym;
    if (e.org !== ePrev.org) {
      break;
    }

    if (e.oNext !== ePrev) {
      // Unlink e from its current position, and relink below ePrev
      libtess.mesh.meshSplice(e.oPrev(), e);
      libtess.mesh.meshSplice(ePrev.oPrev(), e);
    }
    // Compute the winding number and "inside" flag for the new regions
    reg.windingNumber = regPrev.windingNumber - e.winding;
    reg.inside = libtess.sweep.isWindingInside_(tess, reg.windingNumber);

    // Check for two outgoing edges with same slope -- process these
    // before any intersection tests (see example in libtess.sweep.computeInterior).
    regPrev.dirty = true;
    if (!firstTime && libtess.sweep.checkForRightSplice_(tess, regPrev)) {
      libtess.sweep.addWinding_(e, ePrev);
      libtess.sweep.deleteRegion_(tess, regPrev); // TODO(bckenny): need to null regPrev anywhere else?
      libtess.mesh.deleteEdge(ePrev);
    }
    firstTime = false;
    regPrev = reg;
    ePrev = e;
  }

  regPrev.dirty = true;

  if (cleanUp) {
    // Check for intersections between newly adjacent edges.
    libtess.sweep.walkDirtyRegions_(tess, regPrev);
  }
};


/**
 * Set up data for and call GLU_TESS_COMBINE callback on GluTesselator.
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {!libtess.GluVertex} isect A raw vertex at the intersection.
 * @param {!Array<Object>} data The vertices of the intersecting edges.
 * @param {!Array<number>} weights The linear combination coefficients for this intersection.
 * @param {boolean} needed Whether a returned vertex is necessary in this case.
 */
libtess.sweep.callCombine_ = function(tess, isect, data, weights, needed) {
  // Copy coord data in case the callback changes it.
  var coords = [
    isect.coords[0],
    isect.coords[1],
    isect.coords[2]
  ];

  isect.data = null;
  isect.data = tess.callCombineCallback(coords, data, weights);
  if (isect.data === null) {
    if (!needed) {
      // not needed, so just use data from first vertex
      isect.data = data[0];

    } else if (!tess.fatalError) {
      // The only way fatal error is when two edges are found to intersect,
      // but the user has not provided the callback necessary to handle
      // generated intersection points.
      tess.callErrorCallback(libtess.errorType.GLU_TESS_NEED_COMBINE_CALLBACK);
      tess.fatalError = true;
    }
  }
};


/**
 * Two vertices with idential coordinates are combined into one.
 * e1.org is kept, while e2.org is discarded.
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {libtess.GluHalfEdge} e1 [description].
 * @param {libtess.GluHalfEdge} e2 [description].
 */
libtess.sweep.spliceMergeVertices_ = function(tess, e1, e2) {
  // TODO(bckenny): better way to init these? save them?
  var data = [null, null, null, null];
  var weights = [0.5, 0.5, 0, 0];

  data[0] = e1.org.data;
  data[1] = e2.org.data;
  libtess.sweep.callCombine_(tess, e1.org, data, weights, false);
  libtess.mesh.meshSplice(e1, e2);
};


/**
 * Find some weights which describe how the intersection vertex is
 * a linear combination of org and dst. Each of the two edges
 * which generated "isect" is allocated 50% of the weight; each edge
 * splits the weight between its org and dst according to the
 * relative distance to "isect".
 *
 * @private
 * @param {libtess.GluVertex} isect [description].
 * @param {libtess.GluVertex} org [description].
 * @param {libtess.GluVertex} dst [description].
 * @param {Array.<number>} weights [description].
 * @param {number} weightIndex Index into weights for first weight to supply.
 */
libtess.sweep.vertexWeights_ = function(isect, org, dst, weights, weightIndex) {
  // TODO(bckenny): think through how we can use L1dist here and be correct for coords
  var t1 = libtess.geom.vertL1dist(org, isect);
  var t2 = libtess.geom.vertL1dist(dst, isect);

  // TODO(bckenny): introduced weightIndex to mimic addressing in original
  // 1) document (though it is private and only used from getIntersectData)
  // 2) better way? manually inline into getIntersectData? supply two two-length tmp arrays?
  var i0 = weightIndex;
  var i1 = weightIndex + 1;
  weights[i0] = 0.5 * t2 / (t1 + t2);
  weights[i1] = 0.5 * t1 / (t1 + t2);
  isect.coords[0] += weights[i0] * org.coords[0] + weights[i1] * dst.coords[0];
  isect.coords[1] += weights[i0] * org.coords[1] + weights[i1] * dst.coords[1];
  isect.coords[2] += weights[i0] * org.coords[2] + weights[i1] * dst.coords[2];
};


/**
 * We've computed a new intersection point, now we need a "data" pointer
 * from the user so that we can refer to this new vertex in the
 * rendering callbacks.
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {libtess.GluVertex} isect [description].
 * @param {libtess.GluVertex} orgUp [description].
 * @param {libtess.GluVertex} dstUp [description].
 * @param {libtess.GluVertex} orgLo [description].
 * @param {libtess.GluVertex} dstLo [description].
 */
libtess.sweep.getIntersectData_ = function(tess, isect, orgUp, dstUp, orgLo,
    dstLo) {

  // TODO(bckenny): called for every intersection event, should these be from a pool?
  // TODO(bckenny): better way to init these?
  var weights = [0, 0, 0, 0];
  var data = [
    orgUp.data,
    dstUp.data,
    orgLo.data,
    dstLo.data
  ];

  // TODO(bckenny): it appears isect is a reappropriated vertex, so does need to be zeroed.
  // double check this.
  isect.coords[0] = isect.coords[1] = isect.coords[2] = 0;

  // TODO(bckenny): see note in libtess.sweep.vertexWeights_ for explanation of weightIndex. fix?
  libtess.sweep.vertexWeights_(isect, orgUp, dstUp, weights, 0);
  libtess.sweep.vertexWeights_(isect, orgLo, dstLo, weights, 2);

  libtess.sweep.callCombine_(tess, isect, data, weights, true);
};


/**
 * Check the upper and lower edge of regUp, to make sure that the
 * eUp.org is above eLo, or eLo.org is below eUp (depending on which
 * origin is leftmost).
 *
 * The main purpose is to splice right-going edges with the same
 * dest vertex and nearly identical slopes (ie. we can't distinguish
 * the slopes numerically). However the splicing can also help us
 * to recover from numerical errors. For example, suppose at one
 * point we checked eUp and eLo, and decided that eUp.org is barely
 * above eLo. Then later, we split eLo into two edges (eg. from
 * a splice operation like this one). This can change the result of
 * our test so that now eUp.org is incident to eLo, or barely below it.
 * We must correct this condition to maintain the dictionary invariants.
 *
 * One possibility is to check these edges for intersection again
 * (i.e. checkForIntersect). This is what we do if possible. However
 * checkForIntersect requires that tess.event lies between eUp and eLo,
 * so that it has something to fall back on when the intersection
 * calculation gives us an unusable answer. So, for those cases where
 * we can't check for intersection, this routine fixes the problem
 * by just splicing the offending vertex into the other edge.
 * This is a guaranteed solution, no matter how degenerate things get.
 * Basically this is a combinatorial solution to a numerical problem.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regUp [description].
 * @return {boolean} [description].
 */
libtess.sweep.checkForRightSplice_ = function(tess, regUp) {
  // TODO(bckenny): fully learn how these two checks work

  var regLo = regUp.regionBelow();
  var eUp = regUp.eUp;
  var eLo = regLo.eUp;

  if (libtess.geom.vertLeq(eUp.org, eLo.org)) {
    if (libtess.geom.edgeSign(eLo.dst(), eUp.org, eLo.org) > 0) {
      return false;
    }

    // eUp.org appears to be below eLo
    if (!libtess.geom.vertEq(eUp.org, eLo.org)) {
      // Splice eUp.org into eLo
      libtess.mesh.splitEdge(eLo.sym);
      libtess.mesh.meshSplice(eUp, eLo.oPrev());
      regUp.dirty = regLo.dirty = true;

    } else if (eUp.org !== eLo.org) {
      // merge the two vertices, discarding eUp.org
      tess.pq.remove(eUp.org.pqHandle);
      libtess.sweep.spliceMergeVertices_(tess, eLo.oPrev(), eUp);
    }

  } else {
    if (libtess.geom.edgeSign(eUp.dst(), eLo.org, eUp.org) < 0) {
      return false;
    }

    // eLo.org appears to be above eUp, so splice eLo.org into eUp
    regUp.regionAbove().dirty = regUp.dirty = true;
    libtess.mesh.splitEdge(eUp.sym);
    libtess.mesh.meshSplice(eLo.oPrev(), eUp);
  }

  return true;
};


/**
 * Check the upper and lower edge of regUp to make sure that the
 * eUp.dst() is above eLo, or eLo.dst() is below eUp (depending on which
 * destination is rightmost).
 *
 * Theoretically, this should always be true. However, splitting an edge
 * into two pieces can change the results of previous tests. For example,
 * suppose at one point we checked eUp and eLo, and decided that eUp.dst()
 * is barely above eLo. Then later, we split eLo into two edges (eg. from
 * a splice operation like this one). This can change the result of
 * the test so that now eUp.dst() is incident to eLo, or barely below it.
 * We must correct this condition to maintain the dictionary invariants
 * (otherwise new edges might get inserted in the wrong place in the
 * dictionary, and bad stuff will happen).
 *
 * We fix the problem by just splicing the offending vertex into the
 * other edge.
 *
 * @private
 * @param {libtess.GluTesselator} tess description].
 * @param {libtess.ActiveRegion} regUp [description].
 * @return {boolean} [description].
 */
libtess.sweep.checkForLeftSplice_ = function(tess, regUp) {
  var regLo = regUp.regionBelow();
  var eUp = regUp.eUp;
  var eLo = regLo.eUp;
  var e;

  if (libtess.geom.vertLeq(eUp.dst(), eLo.dst())) {
    if (libtess.geom.edgeSign(eUp.dst(), eLo.dst(), eUp.org) < 0) {
      return false;
    }

    // eLo.dst() is above eUp, so splice eLo.dst() into eUp
    regUp.regionAbove().dirty = regUp.dirty = true;
    e = libtess.mesh.splitEdge(eUp);
    libtess.mesh.meshSplice(eLo.sym, e);
    e.lFace.inside = regUp.inside;

  } else {
    if (libtess.geom.edgeSign(eLo.dst(), eUp.dst(), eLo.org) > 0) {
      return false;
    }

    // eUp.dst() is below eLo, so splice eUp.dst() into eLo
    regUp.dirty = regLo.dirty = true;
    e = libtess.mesh.splitEdge(eLo);
    libtess.mesh.meshSplice(eUp.lNext, eLo.sym);
    e.rFace().inside = regUp.inside;
  }

  return true;
};


/**
 * Check the upper and lower edges of the given region to see if
 * they intersect. If so, create the intersection and add it
 * to the data structures.
 *
 * Returns true if adding the new intersection resulted in a recursive
 * call to addRightEdges_(); in this case all "dirty" regions have been
 * checked for intersections, and possibly regUp has been deleted.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regUp [description].
 * @return {boolean} [description].
 */
libtess.sweep.checkForIntersect_ = function(tess, regUp) {
  var regLo = regUp.regionBelow();
  var eUp = regUp.eUp;
  var eLo = regLo.eUp;
  var orgUp = eUp.org;
  var orgLo = eLo.org;
  var dstUp = eUp.dst();
  var dstLo = eLo.dst();

  var isect = new libtess.GluVertex();

  if (orgUp === orgLo) {
    // right endpoints are the same
    return false;
  }

  var tMinUp = Math.min(orgUp.t, dstUp.t);
  var tMaxLo = Math.max(orgLo.t, dstLo.t);
  if (tMinUp > tMaxLo) {
    // t ranges do not overlap
    return false;
  }

  if (libtess.geom.vertLeq(orgUp, orgLo)) {
    if (libtess.geom.edgeSign(dstLo, orgUp, orgLo) > 0) {
      return false;
    }
  } else {
    if (libtess.geom.edgeSign(dstUp, orgLo, orgUp) < 0) {
      return false;
    }
  }

  // At this point the edges intersect, at least marginally
  libtess.geom.edgeIntersect(dstUp, orgUp, dstLo, orgLo, isect);

  // The following properties are guaranteed:

  if (libtess.geom.vertLeq(isect, tess.event)) {
    /* The intersection point lies slightly to the left of the sweep line,
     * so move it until it's slightly to the right of the sweep line.
     * (If we had perfect numerical precision, this would never happen
     * in the first place). The easiest and safest thing to do is
     * replace the intersection by tess.event.
     */
    isect.s = tess.event.s;
    isect.t = tess.event.t;
  }

  // TODO(bckenny): try to find test54.d
  /* Similarly, if the computed intersection lies to the right of the
   * rightmost origin (which should rarely happen), it can cause
   * unbelievable inefficiency on sufficiently degenerate inputs.
   * (If you have the test program, try running test54.d with the
   * "X zoom" option turned on).
   */
  var orgMin = libtess.geom.vertLeq(orgUp, orgLo) ? orgUp : orgLo;
  if (libtess.geom.vertLeq(orgMin, isect)) {
    isect.s = orgMin.s;
    isect.t = orgMin.t;
  }

  if (libtess.geom.vertEq(isect, orgUp) || libtess.geom.vertEq(isect, orgLo)) {
    // Easy case -- intersection at one of the right endpoints
    libtess.sweep.checkForRightSplice_(tess, regUp);
    return false;
  }

  // TODO(bckenny): clean this up; length is distracting
  if ((!libtess.geom.vertEq(dstUp, tess.event) &&
      libtess.geom.edgeSign(dstUp, tess.event, isect) >= 0) ||
      (!libtess.geom.vertEq(dstLo, tess.event) &&
      libtess.geom.edgeSign(dstLo, tess.event, isect) <= 0)) {

    /* Very unusual -- the new upper or lower edge would pass on the
     * wrong side of the sweep event, or through it. This can happen
     * due to very small numerical errors in the intersection calculation.
     */
    if (dstLo === tess.event) {
      // Splice dstLo into eUp, and process the new region(s)
      libtess.mesh.splitEdge(eUp.sym);
      libtess.mesh.meshSplice(eLo.sym, eUp);
      regUp = libtess.sweep.topLeftRegion_(regUp);
      eUp = regUp.regionBelow().eUp;
      libtess.sweep.finishLeftRegions_(tess, regUp.regionBelow(), regLo);
      libtess.sweep.addRightEdges_(tess, regUp, eUp.oPrev(), eUp, eUp, true);
      return true;
    }

    if (dstUp === tess.event) {
      // Splice dstUp into eLo, and process the new region(s)
      libtess.mesh.splitEdge(eLo.sym);
      libtess.mesh.meshSplice(eUp.lNext, eLo.oPrev());
      regLo = regUp;
      regUp = libtess.sweep.topRightRegion_(regUp);
      var e = regUp.regionBelow().eUp.rPrev();
      regLo.eUp = eLo.oPrev();
      eLo = libtess.sweep.finishLeftRegions_(tess, regLo, null);
      libtess.sweep.addRightEdges_(tess, regUp, eLo.oNext, eUp.rPrev(), e,
          true);
      return true;
    }

    /* Special case: called from connectRightVertex. If either
     * edge passes on the wrong side of tess.event, split it
     * (and wait for connectRightVertex to splice it appropriately).
     */
    if (libtess.geom.edgeSign(dstUp, tess.event, isect) >= 0) {
      regUp.regionAbove().dirty = regUp.dirty = true;
      libtess.mesh.splitEdge(eUp.sym);
      eUp.org.s = tess.event.s;
      eUp.org.t = tess.event.t;
    }

    if (libtess.geom.edgeSign(dstLo, tess.event, isect) <= 0) {
      regUp.dirty = regLo.dirty = true;
      libtess.mesh.splitEdge(eLo.sym);
      eLo.org.s = tess.event.s;
      eLo.org.t = tess.event.t;
    }

    // leave the rest for connectRightVertex
    return false;
  }

  /* General case -- split both edges, splice into new vertex.
   * When we do the splice operation, the order of the arguments is
   * arbitrary as far as correctness goes. However, when the operation
   * creates a new face, the work done is proportional to the size of
   * the new face. We expect the faces in the processed part of
   * the mesh (ie. eUp.lFace) to be smaller than the faces in the
   * unprocessed original contours (which will be eLo.oPrev.lFace).
   */
  libtess.mesh.splitEdge(eUp.sym);
  libtess.mesh.splitEdge(eLo.sym);
  libtess.mesh.meshSplice(eLo.oPrev(), eUp);
  eUp.org.s = isect.s;
  eUp.org.t = isect.t;
  eUp.org.pqHandle = tess.pq.insert(eUp.org);
  libtess.sweep.getIntersectData_(tess, eUp.org, orgUp, dstUp, orgLo, dstLo);
  regUp.regionAbove().dirty = regUp.dirty = regLo.dirty = true;

  return false;
};


/**
 * When the upper or lower edge of any region changes, the region is
 * marked "dirty". This routine walks through all the dirty regions
 * and makes sure that the dictionary invariants are satisfied
 * (see the comments at the beginning of this file). Of course,
 * new dirty regions can be created as we make changes to restore
 * the invariants.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regUp [description].
 */
libtess.sweep.walkDirtyRegions_ = function(tess, regUp) {
  var regLo = regUp.regionBelow();

  for (;;) {
    // Find the lowest dirty region (we walk from the bottom up).
    while (regLo.dirty) {
      regUp = regLo;
      regLo = regLo.regionBelow();
    }
    if (!regUp.dirty) {
      regLo = regUp;
      regUp = regUp.regionAbove();
      if (regUp === null || !regUp.dirty) {
        // We've walked all the dirty regions
        return;
      }
    }

    regUp.dirty = false;
    var eUp = regUp.eUp;
    var eLo = regLo.eUp;

    if (eUp.dst() !== eLo.dst()) {
      // Check that the edge ordering is obeyed at the dst vertices.
      if (libtess.sweep.checkForLeftSplice_(tess, regUp)) {
        // If the upper or lower edge was marked fixUpperEdge, then
        // we no longer need it (since these edges are needed only for
        // vertices which otherwise have no right-going edges).
        if (regLo.fixUpperEdge) {
          libtess.sweep.deleteRegion_(tess, regLo);
          libtess.mesh.deleteEdge(eLo);
          regLo = regUp.regionBelow();
          eLo = regLo.eUp;

        } else if (regUp.fixUpperEdge) {
          libtess.sweep.deleteRegion_(tess, regUp);
          libtess.mesh.deleteEdge(eUp);
          regUp = regLo.regionAbove();
          eUp = regUp.eUp;
        }
      }
    }

    if (eUp.org !== eLo.org) {
      if (eUp.dst() !== eLo.dst() && !regUp.fixUpperEdge &&
          !regLo.fixUpperEdge &&
          (eUp.dst() === tess.event || eLo.dst() === tess.event)) {
        /* When all else fails in checkForIntersect(), it uses tess.event
         * as the intersection location. To make this possible, it requires
         * that tess.event lie between the upper and lower edges, and also
         * that neither of these is marked fixUpperEdge (since in the worst
         * case it might splice one of these edges into tess.event, and
         * violate the invariant that fixable edges are the only right-going
         * edge from their associated vertex).
         */
        if (libtess.sweep.checkForIntersect_(tess, regUp)) {
          // walkDirtyRegions() was called recursively; we're done
          return;
        }

      } else {
        // Even though we can't use checkForIntersect(), the org vertices
        // may violate the dictionary edge ordering. Check and correct this.
        libtess.sweep.checkForRightSplice_(tess, regUp);
      }
    }

    if (eUp.org === eLo.org && eUp.dst() === eLo.dst()) {
      // A degenerate loop consisting of only two edges -- delete it.
      libtess.sweep.addWinding_(eLo, eUp);
      libtess.sweep.deleteRegion_(tess, regUp);
      libtess.mesh.deleteEdge(eUp);
      regUp = regLo.regionAbove();
    }
  }
};


/**
 * Purpose: connect a "right" vertex vEvent (one where all edges go left)
 * to the unprocessed portion of the mesh. Since there are no right-going
 * edges, two regions (one above vEvent and one below) are being merged
 * into one. regUp is the upper of these two regions.
 *
 * There are two reasons for doing this (adding a right-going edge):
 *  - if the two regions being merged are "inside", we must add an edge
 *    to keep them separated (the combined region would not be monotone).
 *  - in any case, we must leave some record of vEvent in the dictionary,
 *    so that we can merge vEvent with features that we have not seen yet.
 *    For example, maybe there is a vertical edge which passes just to
 *    the right of vEvent; we would like to splice vEvent into this edge.
 *
 * However, we don't want to connect vEvent to just any vertex. We don't
 * want the new edge to cross any other edges; otherwise we will create
 * intersection vertices even when the input data had no self-intersections.
 * (This is a bad thing; if the user's input data has no intersections,
 * we don't want to generate any false intersections ourselves.)
 *
 * Our eventual goal is to connect vEvent to the leftmost unprocessed
 * vertex of the combined region (the union of regUp and regLo).
 * But because of unseen vertices with all right-going edges, and also
 * new vertices which may be created by edge intersections, we don't
 * know where that leftmost unprocessed vertex is. In the meantime, we
 * connect vEvent to the closest vertex of either chain, and mark the region
 * as "fixUpperEdge". This flag says to delete and reconnect this edge
 * to the next processed vertex on the boundary of the combined region.
 * Quite possibly the vertex we connected to will turn out to be the
 * closest one, in which case we won't need to make any changes.
 *
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.ActiveRegion} regUp [description].
 * @param {libtess.GluHalfEdge} eBottomLeft [description].
 */
libtess.sweep.connectRightVertex_ = function(tess, regUp, eBottomLeft) {
  var eTopLeft = eBottomLeft.oNext;
  var regLo = regUp.regionBelow();
  var eUp = regUp.eUp;
  var eLo = regLo.eUp;
  var degenerate = false;

  if (eUp.dst() !== eLo.dst()) {
    libtess.sweep.checkForIntersect_(tess, regUp);
  }

  // Possible new degeneracies: upper or lower edge of regUp may pass
  // through vEvent, or may coincide with new intersection vertex
  if (libtess.geom.vertEq(eUp.org, tess.event)) {
    libtess.mesh.meshSplice(eTopLeft.oPrev(), eUp);
    regUp = libtess.sweep.topLeftRegion_(regUp);
    eTopLeft = regUp.regionBelow().eUp;
    libtess.sweep.finishLeftRegions_(tess, regUp.regionBelow(), regLo);
    degenerate = true;
  }
  if (libtess.geom.vertEq(eLo.org, tess.event)) {
    libtess.mesh.meshSplice(eBottomLeft, eLo.oPrev());
    eBottomLeft = libtess.sweep.finishLeftRegions_(tess, regLo, null);
    degenerate = true;
  }
  if (degenerate) {
    libtess.sweep.addRightEdges_(tess, regUp, eBottomLeft.oNext, eTopLeft,
        eTopLeft, true);
    return;
  }

  // Non-degenerate situation -- need to add a temporary, fixable edge.
  // Connect to the closer of eLo.org, eUp.org.
  var eNew;
  if (libtess.geom.vertLeq(eLo.org, eUp.org)) {
    eNew = eLo.oPrev();
  } else {
    eNew = eUp;
  }
  eNew = libtess.mesh.connect(eBottomLeft.lPrev(), eNew);

  // Prevent cleanup, otherwise eNew might disappear before we've even
  // had a chance to mark it as a temporary edge.
  libtess.sweep.addRightEdges_(tess, regUp, eNew, eNew.oNext, eNew.oNext,
      false);
  eNew.sym.activeRegion.fixUpperEdge = true;
  libtess.sweep.walkDirtyRegions_(tess, regUp);
};


/**
 * The event vertex lies exacty on an already-processed edge or vertex.
 * Adding the new vertex involves splicing it into the already-processed
 * part of the mesh.
 * @private
 * @param {!libtess.GluTesselator} tess
 * @param {libtess.ActiveRegion} regUp [description].
 * @param {libtess.GluVertex} vEvent [description].
 */
libtess.sweep.connectLeftDegenerate_ = function(tess, regUp, vEvent) {
  var e = regUp.eUp;
  /* istanbul ignore if */
  if (libtess.geom.vertEq(e.org, vEvent)) {
    // NOTE(bckenny): this code is unreachable but remains for a hypothetical
    // future extension of libtess. See docs on libtess.sweep.TOLERANCE_NONZERO_
    // for more information. Conditional on TOLERANCE_NONZERO_ to help Closure
    // Compiler eliminate dead code.
    // e.org is an unprocessed vertex - just combine them, and wait
    // for e.org to be pulled from the queue
    if (libtess.sweep.TOLERANCE_NONZERO_) {
      libtess.sweep.spliceMergeVertices_(tess, e, vEvent.anEdge);
    }
    return;
  }

  if (!libtess.geom.vertEq(e.dst(), vEvent)) {
    // General case -- splice vEvent into edge e which passes through it
    libtess.mesh.splitEdge(e.sym);

    if (regUp.fixUpperEdge) {
      // This edge was fixable -- delete unused portion of original edge
      libtess.mesh.deleteEdge(e.oNext);
      regUp.fixUpperEdge = false;
    }

    libtess.mesh.meshSplice(vEvent.anEdge, e);

    // recurse
    libtess.sweep.sweepEvent_(tess, vEvent);
    return;
  }

  // NOTE(bckenny): this code is unreachable but remains for a hypothetical
  // future extension of libtess. See docs on libtess.sweep.TOLERANCE_NONZERO_
  // for more information. Conditional on TOLERANCE_NONZERO_ to help Closure
  // Compiler eliminate dead code.
  // vEvent coincides with e.dst(), which has already been processed.
  // Splice in the additional right-going edges.
  /* istanbul ignore next */

  /* istanbul ignore next */
  if (libtess.sweep.TOLERANCE_NONZERO_) {
    regUp = libtess.sweep.topRightRegion_(regUp);
    var reg = regUp.regionBelow();
    var eTopRight = reg.eUp.sym;
    var eTopLeft = eTopRight.oNext;
    var eLast = eTopLeft;

    if (reg.fixUpperEdge) {
      // Here e.dst() has only a single fixable edge going right.
      // We can delete it since now we have some real right-going edges.

      // there are some left edges too
      libtess.sweep.deleteRegion_(tess, reg); // TODO(bckenny): something to null?
      libtess.mesh.deleteEdge(eTopRight);
      eTopRight = eTopLeft.oPrev();
    }

    libtess.mesh.meshSplice(vEvent.anEdge, eTopRight);
    if (!libtess.geom.edgeGoesLeft(eTopLeft)) {
      // e.dst() had no left-going edges -- indicate this to addRightEdges()
      eTopLeft = null;
    }

    libtess.sweep.addRightEdges_(tess, regUp, eTopRight.oNext, eLast, eTopLeft,
        true);
  }
};


/**
 * Connect a "left" vertex (one where both edges go right)
 * to the processed portion of the mesh. Let R be the active region
 * containing vEvent, and let U and L be the upper and lower edge
 * chains of R. There are two possibilities:
 *
 * - the normal case: split R into two regions, by connecting vEvent to
 *   the rightmost vertex of U or L lying to the left of the sweep line
 *
 * - the degenerate case: if vEvent is close enough to U or L, we
 *   merge vEvent into that edge chain. The subcases are:
 *  - merging with the rightmost vertex of U or L
 *  - merging with the active edge of U or L
 *  - merging with an already-processed portion of U or L
 *
 * @private
 * @param {libtess.GluTesselator} tess   [description].
 * @param {libtess.GluVertex} vEvent [description].
 */
libtess.sweep.connectLeftVertex_ = function(tess, vEvent) {
  // TODO(bckenny): tmp only used for sweep. better to keep tmp across calls?
  var tmp = new libtess.ActiveRegion();

  // NOTE(bckenny): this was commented out in the original
  // libtess.assert(vEvent.anEdge.oNext.oNext === vEvent.anEdge);

  // Get a pointer to the active region containing vEvent
  tmp.eUp = vEvent.anEdge.sym;
  var regUp = tess.dict.search(tmp).getKey();
  var regLo = regUp.regionBelow();
  var eUp = regUp.eUp;
  var eLo = regLo.eUp;

  // try merging with U or L first
  if (libtess.geom.edgeSign(eUp.dst(), vEvent, eUp.org) === 0) {
    libtess.sweep.connectLeftDegenerate_(tess, regUp, vEvent);
    return;
  }

  // Connect vEvent to rightmost processed vertex of either chain.
  // e.dst() is the vertex that we will connect to vEvent.
  var reg = libtess.geom.vertLeq(eLo.dst(), eUp.dst()) ? regUp : regLo;
  var eNew;
  if (regUp.inside || reg.fixUpperEdge) {
    if (reg === regUp) {
      eNew = libtess.mesh.connect(vEvent.anEdge.sym, eUp.lNext);

    } else {
      var tempHalfEdge = libtess.mesh.connect(eLo.dNext(), vEvent.anEdge);
      eNew = tempHalfEdge.sym;
    }

    if (reg.fixUpperEdge) {
      libtess.sweep.fixUpperEdge_(reg, eNew);

    } else {
      libtess.sweep.computeWinding_(tess,
          libtess.sweep.addRegionBelow_(tess, regUp, eNew));
    }
    libtess.sweep.sweepEvent_(tess, vEvent);

  } else {
    // The new vertex is in a region which does not belong to the polygon.
    // We don''t need to connect this vertex to the rest of the mesh.
    libtess.sweep.addRightEdges_(tess, regUp, vEvent.anEdge, vEvent.anEdge,
        null, true);
  }
};


/**
 * Does everything necessary when the sweep line crosses a vertex.
 * Updates the mesh and the edge dictionary.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {libtess.GluVertex} vEvent [description].
 */
libtess.sweep.sweepEvent_ = function(tess, vEvent) {
  tess.event = vEvent; // for access in edgeLeq_ // TODO(bckenny): wuh?

  /* Check if this vertex is the right endpoint of an edge that is
   * already in the dictionary.  In this case we don't need to waste
   * time searching for the location to insert new edges.
   */
  var e = vEvent.anEdge;
  while (e.activeRegion === null) {
    e = e.oNext;
    if (e === vEvent.anEdge) {
      // All edges go right -- not incident to any processed edges
      libtess.sweep.connectLeftVertex_(tess, vEvent);
      return;
    }
  }

  /* Processing consists of two phases: first we "finish" all the
   * active regions where both the upper and lower edges terminate
   * at vEvent (ie. vEvent is closing off these regions).
   * We mark these faces "inside" or "outside" the polygon according
   * to their winding number, and delete the edges from the dictionary.
   * This takes care of all the left-going edges from vEvent.
   */
  var regUp = libtess.sweep.topLeftRegion_(e.activeRegion);
  var reg = regUp.regionBelow();
  var eTopLeft = reg.eUp;
  var eBottomLeft = libtess.sweep.finishLeftRegions_(tess, reg, null);

  /* Next we process all the right-going edges from vEvent. This
   * involves adding the edges to the dictionary, and creating the
   * associated "active regions" which record information about the
   * regions between adjacent dictionary edges.
   */
  if (eBottomLeft.oNext === eTopLeft) {
    // No right-going edges -- add a temporary "fixable" edge
    libtess.sweep.connectRightVertex_(tess, regUp, eBottomLeft);

  } else {
    libtess.sweep.addRightEdges_(tess, regUp, eBottomLeft.oNext, eTopLeft,
        eTopLeft, true);
  }
};


/**
 * We add two sentinel edges above and below all other edges,
 * to avoid special cases at the top and bottom.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 * @param {number} t [description].
 */
libtess.sweep.addSentinel_ = function(tess, t) {
  var reg = new libtess.ActiveRegion();

  var e = libtess.mesh.makeEdge(tess.mesh);

  e.org.s = libtess.sweep.SENTINEL_COORD_;
  e.org.t = t;
  e.dst().s = -libtess.sweep.SENTINEL_COORD_;
  e.dst().t = t;
  tess.event = e.dst(); //initialize it

  reg.eUp = e;
  reg.windingNumber = 0;
  reg.inside = false;
  reg.fixUpperEdge = false;
  reg.sentinel = true;
  reg.dirty = false;
  reg.nodeUp = tess.dict.insert(reg);
};


/**
 * We maintain an ordering of edge intersections with the sweep line.
 * This order is maintained in a dynamic dictionary.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.initEdgeDict_ = function(tess) {
  tess.dict = new libtess.Dict(tess, libtess.sweep.edgeLeq_);

  libtess.sweep.addSentinel_(tess, -libtess.sweep.SENTINEL_COORD_);
  libtess.sweep.addSentinel_(tess, libtess.sweep.SENTINEL_COORD_);
};


/**
 * [doneEdgeDict_ description]
 * @private
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.doneEdgeDict_ = function(tess) {
  // NOTE(bckenny): fixedEdges is only used in the assert below, so ignore so
  // when asserts are removed jshint won't error.
  /* jshint unused:false */
  var fixedEdges = 0;

  var reg;
  while ((reg = tess.dict.getMin().getKey()) !== null) {
    // At the end of all processing, the dictionary should contain
    // only the two sentinel edges, plus at most one "fixable" edge
    // created by connectRightVertex().
    if (!reg.sentinel) {
    }
    libtess.sweep.deleteRegion_(tess, reg);
  }

  // NOTE(bckenny): see tess.dict.deleteDict_() for old delete dict function
  tess.dict = null;
};


/**
 * Remove zero-length edges, and contours with fewer than 3 vertices.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.removeDegenerateEdges_ = function(tess) {
  var eHead = tess.mesh.eHead;

  var eNext;
  for (var e = eHead.next; e !== eHead; e = eNext) {
    eNext = e.next;
    var eLNext = e.lNext;

    if (libtess.geom.vertEq(e.org, e.dst()) && e.lNext.lNext !== e) {
      // Zero-length edge, contour has at least 3 edges
      libtess.sweep.spliceMergeVertices_(tess, eLNext, e); // deletes e.org
      libtess.mesh.deleteEdge(e); // e is a self-loop TODO(bckenny): does this comment really apply here?
      e = eLNext;
      eLNext = e.lNext;
    }

    if (eLNext.lNext === e) {
      // Degenerate contour (one or two edges)
      if (eLNext !== e) {
        if (eLNext === eNext || eLNext === eNext.sym) {
          eNext = eNext.next;
        }
        libtess.mesh.deleteEdge(eLNext);
      }

      if (e === eNext || e === eNext.sym) {
        eNext = eNext.next;
      }
      libtess.mesh.deleteEdge(e);
    }
  }
};


/**
 * Construct priority queue and insert all vertices into it, which determines
 * the order in which vertices cross the sweep line.
 * @private
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.initPriorityQ_ = function(tess) {
  var pq = new libtess.PriorityQ();
  tess.pq = pq;

  var vHead = tess.mesh.vHead;
  var v;
  for (v = vHead.next; v !== vHead; v = v.next) {
    v.pqHandle = pq.insert(v);
  }

  pq.init();
};


/**
 * [donePriorityQ_ description]
 * @private
 * @param {libtess.GluTesselator} tess [description].
 */
libtess.sweep.donePriorityQ_ = function(tess) {
  // TODO(bckenny): probably don't need deleteQ. check that function for comment
  tess.pq.deleteQ();
  tess.pq = null;
};


/**
 * Delete any degenerate faces with only two edges. walkDirtyRegions()
 * will catch almost all of these, but it won't catch degenerate faces
 * produced by splice operations on already-processed edges.
 * The two places this can happen are in finishLeftRegions(), when
 * we splice in a "temporary" edge produced by connectRightVertex(),
 * and in checkForLeftSplice(), where we splice already-processed
 * edges to ensure that our dictionary invariants are not violated
 * by numerical errors.
 *
 * In both these cases it is *very* dangerous to delete the offending
 * edge at the time, since one of the routines further up the stack
 * will sometimes be keeping a pointer to that edge.
 *
 * @private
 * @param {libtess.GluMesh} mesh [description].
 */
libtess.sweep.removeDegenerateFaces_ = function(mesh) {
  var fNext;
  for (var f = mesh.fHead.next; f !== mesh.fHead; f = fNext) {
    fNext = f.next;
    var e = f.anEdge;

    if (e.lNext.lNext === e) {
      // A face with only two edges
      libtess.sweep.addWinding_(e.oNext, e);
      libtess.mesh.deleteEdge(e);
    }
  }
};

/* global libtess */

/** @const */
libtess.tessmono = {};

/**
 * Tessellates a monotone region (what else would it do??). The region must
 * consist of a single loop of half-edges (see mesh.js) oriented CCW. "Monotone"
 * in this case means that any vertical line intersects the interior of the
 * region in a single interval.
 *
 * Tessellation consists of adding interior edges (actually pairs of
 * half-edges), to split the region into non-overlapping triangles.
 * @private
 * @param {!libtess.GluFace} face
 */
libtess.tessmono.tessellateMonoRegion_ = function(face) {
  /* The basic idea is explained in Preparata and Shamos (which I don't
   * have handy right now), although their implementation is more
   * complicated than this one. The are two edge chains, an upper chain
   * and a lower chain. We process all vertices from both chains in order,
   * from right to left.
   *
   * The algorithm ensures that the following invariant holds after each
   * vertex is processed: the untessellated region consists of two
   * chains, where one chain (say the upper) is a single edge, and
   * the other chain is concave. The left vertex of the single edge
   * is always to the left of all vertices in the concave chain.
   *
   * Each step consists of adding the rightmost unprocessed vertex to one
   * of the two chains, and forming a fan of triangles from the rightmost
   * of two chain endpoints. Determining whether we can add each triangle
   * to the fan is a simple orientation test. By making the fan as large
   * as possible, we restore the invariant (check it yourself).
   *
   * All edges are oriented CCW around the boundary of the region.
   * First, find the half-edge whose origin vertex is rightmost.
   * Since the sweep goes from left to right, face.anEdge should
   * be close to the edge we want.
   */
  var up = face.anEdge;

  for (; libtess.geom.vertLeq(up.dst(), up.org); up = up.lPrev()) { }
  for (; libtess.geom.vertLeq(up.org, up.dst()); up = up.lNext) { }

  var lo = up.lPrev();

  var tempHalfEdge;
  while (up.lNext !== lo) {
    if (libtess.geom.vertLeq(up.dst(), lo.org)) {
      // up.dst() is on the left. It is safe to form triangles from lo.org.
      // The edgeGoesLeft test guarantees progress even when some triangles
      // are CW, given that the upper and lower chains are truly monotone.
      while (lo.lNext !== up && (libtess.geom.edgeGoesLeft(lo.lNext) ||
          libtess.geom.edgeSign(lo.org, lo.dst(), lo.lNext.dst()) <= 0)) {

        tempHalfEdge = libtess.mesh.connect(lo.lNext, lo);
        lo = tempHalfEdge.sym;
      }
      lo = lo.lPrev();

    } else {
      // lo.org is on the left. We can make CCW triangles from up.dst().
      while (lo.lNext !== up && (libtess.geom.edgeGoesRight(up.lPrev()) ||
          libtess.geom.edgeSign(up.dst(), up.org, up.lPrev().org) >= 0)) {

        tempHalfEdge = libtess.mesh.connect(up, up.lPrev());
        up = tempHalfEdge.sym;
      }
      up = up.lNext;
    }
  }

  // Now lo.org == up.dst() == the leftmost vertex. The remaining region
  // can be tessellated in a fan from this leftmost vertex.
  while (lo.lNext.lNext !== up) {
    tempHalfEdge = libtess.mesh.connect(lo.lNext, lo);
    lo = tempHalfEdge.sym;
  }
};

/**
 * Tessellates each region of the mesh which is marked "inside" the polygon.
 * Each such region must be monotone.
 * @param {!libtess.GluMesh} mesh
 */
libtess.tessmono.tessellateInterior = function(mesh) {
  var next;
  for (var f = mesh.fHead.next; f !== mesh.fHead; f = next) {
    // Make sure we don't try to tessellate the new triangles.
    next = f.next;
    if (f.inside) {
      libtess.tessmono.tessellateMonoRegion_(f);
    }
  }
};

/**
 * Zaps (i.e. sets to null) all faces which are not marked "inside" the polygon.
 * Since further mesh operations on null faces are not allowed, the main purpose
 * is to clean up the mesh so that exterior loops are not represented in the
 * data structure.
 * @param {!libtess.GluMesh} mesh
 */
libtess.tessmono.discardExterior = function(mesh) {
  var next;
  for (var f = mesh.fHead.next; f !== mesh.fHead; f = next) {
    // Since f will be destroyed, save its next pointer.
    next = f.next;
    if (!f.inside) {
      libtess.mesh.zapFace(f);
    }
  }
};

/**
 * Resets the winding numbers on all edges so that regions marked "inside" the
 * polygon have a winding number of "value", and regions outside have a winding
 * number of 0.
 *
 * If keepOnlyBoundary is true, it also deletes all edges which do not separate
 * an interior region from an exterior one.
 *
 * @param {!libtess.GluMesh} mesh
 * @param {number} value
 * @param {boolean} keepOnlyBoundary
 */
libtess.tessmono.setWindingNumber = function(mesh, value, keepOnlyBoundary) {
  var eNext;
  for (var e = mesh.eHead.next; e !== mesh.eHead; e = eNext) {
    eNext = e.next;

    if (e.rFace().inside !== e.lFace.inside) {
      // This is a boundary edge (one side is interior, one is exterior).
      e.winding = (e.lFace.inside) ? value : -value;

    } else {
      // Both regions are interior, or both are exterior.
      if (!keepOnlyBoundary) {
        e.winding = 0;

      } else {
        libtess.mesh.deleteEdge(e);
      }
    }
  }
};

/* global libtess */

/**
 * A list of edges crossing the sweep line, sorted from top to bottom.
 * Implementation is a doubly-linked list, sorted by the injected edgeLeq
 * comparator function. Here it is a simple ordering, but see libtess.sweep for
 * the list of invariants on the edge dictionary this ordering creates.
 * @constructor
 * @struct
 * @param {!libtess.GluTesselator} frame
 * @param {function(!libtess.GluTesselator, !libtess.ActiveRegion, !libtess.ActiveRegion): boolean} leq
 */
libtess.Dict = function(frame, leq) {

  /**
   * The head of the doubly-linked DictNode list. At creation time, links back
   * and forward only to itself.
   * @private {!libtess.DictNode}
   */
  this.head_ = new libtess.DictNode();

  /**
   * The GluTesselator used as the frame for edge/event comparisons.
   * @private {!libtess.GluTesselator}
   */
  this.frame_ = frame;

  /**
   * Comparison function to maintain the invariants of the Dict. See
   * libtess.sweep.edgeLeq_ for source.
   * @private
   * @type {function(!libtess.GluTesselator, !libtess.ActiveRegion, !libtess.ActiveRegion): boolean}
   */
  this.leq_ = leq;
};

/* istanbul ignore next */
/**
 * Formerly used to delete the dict.
 * NOTE(bckenny): No longer called but left for memFree documentation. Nulled at
 * former callsite instead (sweep.doneEdgeDict_)
 * @private
 */
libtess.Dict.prototype.deleteDict_ = function() {
  // for (var node = this.head_.next; node !== this.head_; node = node.next) {
  //   memFree(node);
  // }
  // memFree(dict);
};

/**
 * Insert the supplied key into the edge list and return its new node.
 * @param {libtess.DictNode} node
 * @param {!libtess.ActiveRegion} key
 * @return {!libtess.DictNode}
 */
libtess.Dict.prototype.insertBefore = function(node, key) {
  do {
    node = node.prev;
  } while (node.key !== null && !this.leq_(this.frame_, node.key, key));

  // insert the new node and update the surrounding nodes to point to it
  var newNode = new libtess.DictNode(key, node.next, node);
  node.next.prev = newNode;
  node.next = newNode;

  return newNode;
};

/**
 * Insert key into the dict and return the new node that contains it.
 * @param {!libtess.ActiveRegion} key
 * @return {!libtess.DictNode}
 */
libtess.Dict.prototype.insert = function(key) {
  // NOTE(bckenny): from a macro in dict.h/dict-list.h
  return this.insertBefore(this.head_, key);
};

/**
 * Remove node from the list.
 * @param {libtess.DictNode} node
 */
libtess.Dict.prototype.deleteNode = function(node) {
  node.next.prev = node.prev;
  node.prev.next = node.next;

  // NOTE(bckenny): nulled at callsite (sweep.deleteRegion_)
  // memFree( node );
};

/**
 * Search returns the node with the smallest key greater than or equal
 * to the given key. If there is no such key, returns a node whose
 * key is null. Similarly, max(d).getSuccessor() has a null key, etc.
 * @param {!libtess.ActiveRegion} key
 * @return {!libtess.DictNode}
 */
libtess.Dict.prototype.search = function(key) {
  var node = this.head_;

  do {
    node = node.next;
  } while (node.key !== null && !this.leq_(this.frame_, key, node.key));

  return node;
};

/**
 * Return the node with the smallest key.
 * @return {!libtess.DictNode}
 */
libtess.Dict.prototype.getMin = function() {
  // NOTE(bckenny): from a macro in dict.h/dict-list.h
  return this.head_.next;
};

// NOTE(bckenny): libtess.Dict.getMax isn't called within libtess and isn't part
// of the public API. For now, leaving in but ignoring for coverage.
/* istanbul ignore next */
/**
 * Returns the node with the greatest key.
 * @return {!libtess.DictNode}
 */
libtess.Dict.prototype.getMax = function() {
  // NOTE(bckenny): from a macro in dict.h/dict-list.h
  return this.head_.prev;
};

/* global libtess */

/**
 * A doubly-linked-list node with a libtess.ActiveRegion payload.
 * The key for this node and the next and previous nodes in the parent Dict list
 * can be provided to insert it into an existing list (or all can be omitted if
 * this is to be the founding node of the list).
 * @param {!libtess.ActiveRegion=} opt_key
 * @param {!libtess.DictNode=} opt_nextNode
 * @param {!libtess.DictNode=} opt_prevNode
 * @constructor
 * @struct
 */
libtess.DictNode = function(opt_key, opt_nextNode, opt_prevNode) {
  /**
   * The ActiveRegion key for this node, or null if the head of the list.
   * @type {libtess.ActiveRegion}
   */
  this.key = opt_key || null;

  /**
   * Link to next DictNode in parent list or to self if this is the first node.
   * @type {!libtess.DictNode}
   */
  this.next = opt_nextNode || this;

  /**
   * Link to previous DictNode in parent list or to self if this is the first
   * node.
   * @type {!libtess.DictNode}
   */
  this.prev = opt_prevNode || this;
};

/**
 * Get the key from this node.
 * @return {libtess.ActiveRegion}
 */
libtess.DictNode.prototype.getKey = function() {
  return this.key;
};

/**
 * Get the successor node to this one.
 * @return {!libtess.DictNode}
 */
libtess.DictNode.prototype.getSuccessor = function() {
  return this.next;
};

/**
 * Get the predecessor node to this one.
 * @return {!libtess.DictNode}
 */
libtess.DictNode.prototype.getPredecessor = function() {
  return this.prev;
};

/* global libtess */

// TODO(bckenny): create more javascript-y API, e.g. make gluTessEndPolygon
// async, don't require so many temp objects created

/**
 * The tesselator main class, providing the public API.
 * @constructor
 * @struct
 */
libtess.GluTesselator = function() {
  // Only initialize fields which can be changed by the api. Other fields
  // are initialized where they are used.

  /*** state needed for collecting the input data ***/

  /**
   * Tesselator state, tracking what begin/end calls have been seen.
   * @private {libtess.GluTesselator.tessState_}
   */
  this.state_ = libtess.GluTesselator.tessState_.T_DORMANT;

  /**
   * lastEdge_.org is the most recent vertex
   * @private {libtess.GluHalfEdge}
   */
  this.lastEdge_ = null;

  /**
   * stores the input contours, and eventually the tessellation itself
   * @type {libtess.GluMesh}
   */
  this.mesh = null;

  /**
   * Error callback.
   * @private {?function((libtess.errorType|libtess.gluEnum), Object=)}
   */
  this.errorCallback_ = null;

  /*** state needed for projecting onto the sweep plane ***/

  /**
   * user-specified normal (if provided)
   * @private {!Array<number>}
   */
  this.normal_ = [0, 0, 0];

  /*** state needed for the line sweep ***/

  /**
   * rule for determining polygon interior
   * @type {libtess.windingRule}
   */
  this.windingRule = libtess.windingRule.GLU_TESS_WINDING_ODD;

  /**
   * fatal error: needed combine callback
   * @type {boolean}
   */
  this.fatalError = false;

  /**
   * edge dictionary for sweep line
   * @type {libtess.Dict}
   */
  this.dict = null;
  // NOTE(bckenny): dict initialized in sweep.initEdgeDict_, removed in sweep.doneEdgeDict_

  /**
   * priority queue of vertex events
   * @type {libtess.PriorityQ}
   */
  this.pq = null;
  // NOTE(bckenny): pq initialized in sweep.initPriorityQ

  /**
   * current sweep event being processed
   * @type {libtess.GluVertex}
   */
  this.event = null;

  /**
   * Combine callback.
   * @private {?function(Array<number>, Array<Object>, Array<number>, Object=): Object}
   */
  this.combineCallback_ = null;

  /*** state needed for rendering callbacks (see render.js) ***/

  /**
   * Extract contours, not triangles
   * @private {boolean}
   */
  this.boundaryOnly_ = false;

  /**
   * Begin callback.
   * @private {?function(libtess.primitiveType, Object=)}
   */
  this.beginCallback_ = null;

  /**
   * Edge flag callback.
   * @private {?function(boolean, Object=)}
   */
  this.edgeFlagCallback_ = null;

  /**
   * Vertex callback.
   * @private {?function(Object, Object=)}
   */
  this.vertexCallback_ = null;

  /**
   * End callback.
   * @private {?function(Object=)}
   */
  this.endCallback_ = null;

  /**
   * Mesh callback.
   * @private {?function(libtess.GluMesh)}
   */
  this.meshCallback_ = null;

  /**
   * client data for current polygon
   * @private {Object}
   */
  this.polygonData_ = null;
};

/**
 * The begin/end calls must be properly nested. We keep track of the current
 * state to enforce the ordering.
 * @enum {number}
 * @private
 */
libtess.GluTesselator.tessState_ = {
  T_DORMANT: 0,
  T_IN_POLYGON: 1,
  T_IN_CONTOUR: 2
};

/**
 * Destory the tesselator object. See README.
 */
libtess.GluTesselator.prototype.gluDeleteTess = function() {
  // TODO(bckenny): This does nothing but assert that it isn't called while
  // building the polygon since we rely on GC to handle memory. *If* the public
  // API changes, this should go.
  this.requireState_(libtess.GluTesselator.tessState_.T_DORMANT);
  // memFree(tess); TODO(bckenny)
};

/**
 * Set properties for control over tesselation. See README.
 * @param {libtess.gluEnum} which [description].
 * @param {number|boolean} value [description].
 */
libtess.GluTesselator.prototype.gluTessProperty = function(which, value) {
  // TODO(bckenny): split into more setters?
  // TODO(bckenny): in any case, we can do better than this switch statement

  switch (which) {
    case libtess.gluEnum.GLU_TESS_TOLERANCE:
      // NOTE(bckenny): libtess has never supported any tolerance but 0.
      return;

    case libtess.gluEnum.GLU_TESS_WINDING_RULE:
      var windingRule = /** @type {libtess.windingRule} */(value);

      switch (windingRule) {
        case libtess.windingRule.GLU_TESS_WINDING_ODD:
        case libtess.windingRule.GLU_TESS_WINDING_NONZERO:
        case libtess.windingRule.GLU_TESS_WINDING_POSITIVE:
        case libtess.windingRule.GLU_TESS_WINDING_NEGATIVE:
        case libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO:
          this.windingRule = windingRule;
          return;
        default:
      }
      break;

    case libtess.gluEnum.GLU_TESS_BOUNDARY_ONLY:
      this.boundaryOnly_ = !!value;
      return;

    default:
      this.callErrorCallback(libtess.gluEnum.GLU_INVALID_ENUM);
      return;
  }
  this.callErrorCallback(libtess.gluEnum.GLU_INVALID_VALUE);
};

/**
 * Returns tessellator property
 * @param {libtess.gluEnum} which [description].
 * @return {number|boolean} [description].
 */
libtess.GluTesselator.prototype.gluGetTessProperty = function(which) {
  // TODO(bckenny): as above, split into more getters? and improve on switch statement
  // why are these being asserted in getter but not setter?

  switch (which) {
    case libtess.gluEnum.GLU_TESS_TOLERANCE:
      return 0;

    case libtess.gluEnum.GLU_TESS_WINDING_RULE:
      var rule = this.windingRule;
      return rule;

    case libtess.gluEnum.GLU_TESS_BOUNDARY_ONLY:
      return this.boundaryOnly_;

    default:
      this.callErrorCallback(libtess.gluEnum.GLU_INVALID_ENUM);
      break;
  }
  return false;
};

/**
 * Lets the user supply the polygon normal, if known. All input data is
 * projected into a plane perpendicular to the normal before tesselation. All
 * output triangles are oriented CCW with respect to the normal (CW orientation
 * can be obtained by reversing the sign of the supplied normal). For example,
 * if you know that all polygons lie in the x-y plane, call
 * `tess.gluTessNormal(0.0, 0.0, 1.0)` before rendering any polygons.
 * @param {number} x
 * @param {number} y
 * @param {number} z
 */
libtess.GluTesselator.prototype.gluTessNormal = function(x, y, z) {
  this.normal_[0] = x;
  this.normal_[1] = y;
  this.normal_[2] = z;
};

/**
 * Specify callbacks. See README for callback descriptions. A null or undefined
 * opt_fn removes current callback.
 * @param {libtess.gluEnum} which The callback-type gluEnum value.
 * @param {?Function=} opt_fn
 */
libtess.GluTesselator.prototype.gluTessCallback = function(which, opt_fn) {
  var fn = !opt_fn ? null : opt_fn;
  // TODO(bckenny): better opt_fn typing?
  // TODO(bckenny): should add documentation that references in callback are volatile (or make a copy)

  switch (which) {
    case libtess.gluEnum.GLU_TESS_BEGIN:
    case libtess.gluEnum.GLU_TESS_BEGIN_DATA:
      this.beginCallback_ = /** @type {?function(libtess.primitiveType, Object=)} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_EDGE_FLAG:
    case libtess.gluEnum.GLU_TESS_EDGE_FLAG_DATA:
      this.edgeFlagCallback_ = /** @type {?function(boolean, Object=)} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_VERTEX:
    case libtess.gluEnum.GLU_TESS_VERTEX_DATA:
      this.vertexCallback_ = /** @type {?function(Object, Object=)} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_END:
    case libtess.gluEnum.GLU_TESS_END_DATA:
      this.endCallback_ = /** @type {?function(Object=)} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_ERROR:
    case libtess.gluEnum.GLU_TESS_ERROR_DATA:
      this.errorCallback_ = /** @type {?function((libtess.errorType|libtess.gluEnum), Object=)} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_COMBINE:
    case libtess.gluEnum.GLU_TESS_COMBINE_DATA:
      this.combineCallback_ = /** @type {?function(Array<number>, Array<Object>, Array<number>, Object=): Object} */ (fn);
      return;

    case libtess.gluEnum.GLU_TESS_MESH:
      this.meshCallback_ = /** @type {?function(libtess.GluMesh)} */ (fn);
      return;

    default:
      this.callErrorCallback(libtess.gluEnum.GLU_INVALID_ENUM);
      return;
  }
};

/**
 * Specify a vertex and associated data. Must be within calls to
 * beginContour/endContour. See README.
 * @param {!Array<number>} coords
 * @param {Object} data
 */
libtess.GluTesselator.prototype.gluTessVertex = function(coords, data) {
  var tooLarge = false;

  // TODO(bckenny): pool allocation?
  var clamped = [0, 0, 0];

  this.requireState_(libtess.GluTesselator.tessState_.T_IN_CONTOUR);

  for (var i = 0; i < 3; ++i) {
    var x = coords[i];
    if (x < -libtess.GLU_TESS_MAX_COORD) {
      x = -libtess.GLU_TESS_MAX_COORD;
      tooLarge = true;
    }
    if (x > libtess.GLU_TESS_MAX_COORD) {
      x = libtess.GLU_TESS_MAX_COORD;
      tooLarge = true;
    }
    clamped[i] = x;
  }

  if (tooLarge) {
    this.callErrorCallback(libtess.errorType.GLU_TESS_COORD_TOO_LARGE);
  }

  this.addVertex_(clamped, data);
};

/**
 * [gluTessBeginPolygon description]
 * @param {Object} data Client data for current polygon.
 */
libtess.GluTesselator.prototype.gluTessBeginPolygon = function(data) {
  this.requireState_(libtess.GluTesselator.tessState_.T_DORMANT);

  this.state_ = libtess.GluTesselator.tessState_.T_IN_POLYGON;

  this.mesh = new libtess.GluMesh();

  this.polygonData_ = data;
};

/**
 * [gluTessBeginContour description]
 */
libtess.GluTesselator.prototype.gluTessBeginContour = function() {
  this.requireState_(libtess.GluTesselator.tessState_.T_IN_POLYGON);

  this.state_ = libtess.GluTesselator.tessState_.T_IN_CONTOUR;
  this.lastEdge_ = null;
};

/**
 * [gluTessEndContour description]
 */
libtess.GluTesselator.prototype.gluTessEndContour = function() {
  this.requireState_(libtess.GluTesselator.tessState_.T_IN_CONTOUR);
  this.state_ = libtess.GluTesselator.tessState_.T_IN_POLYGON;
};

/**
 * [gluTessEndPolygon description]
 */
libtess.GluTesselator.prototype.gluTessEndPolygon = function() {
  this.requireState_(libtess.GluTesselator.tessState_.T_IN_POLYGON);
  this.state_ = libtess.GluTesselator.tessState_.T_DORMANT;

  // Determine the polygon normal and project vertices onto the plane
  // of the polygon.
  libtess.normal.projectPolygon(this, this.normal_[0], this.normal_[1],
      this.normal_[2]);

  // computeInterior(tess) computes the planar arrangement specified
  // by the given contours, and further subdivides this arrangement
  // into regions. Each region is marked "inside" if it belongs
  // to the polygon, according to the rule given by this.windingRule.
  // Each interior region is guaranteed be monotone.
  libtess.sweep.computeInterior(this);

  if (!this.fatalError) {
    // If the user wants only the boundary contours, we throw away all edges
    // except those which separate the interior from the exterior.
    // Otherwise we tessellate all the regions marked "inside".
    // NOTE(bckenny): we know this.mesh has been initialized, so help closure out.
    var mesh = /** @type {!libtess.GluMesh} */(this.mesh);
    if (this.boundaryOnly_) {
      libtess.tessmono.setWindingNumber(mesh, 1, true);
    } else {
      libtess.tessmono.tessellateInterior(mesh);
    }

    this.mesh.checkMesh();

    if (this.beginCallback_ || this.endCallback_ || this.vertexCallback_ ||
        this.edgeFlagCallback_) {

      if (this.boundaryOnly_) {
        // output boundary contours
        libtess.render.renderBoundary(this, this.mesh);

      } else {
        // output triangles (with edge callback if one is set)
        var flagEdges = !!this.edgeFlagCallback_;
        libtess.render.renderMesh(this, this.mesh, flagEdges);
      }
    }

    if (this.meshCallback_) {
      // Throw away the exterior faces, so that all faces are interior.
      // This way the user doesn't have to check the "inside" flag,
      // and we don't need to even reveal its existence. It also leaves
      // the freedom for an implementation to not generate the exterior
      // faces in the first place.
      libtess.tessmono.discardExterior(this.mesh);
      // user wants the mesh itself
      this.meshCallback_(this.mesh);

      this.mesh = null;
      this.polygonData_ = null;
      return;
    }
  }

  libtess.mesh.deleteMesh(this.mesh);
  this.polygonData_ = null;
  this.mesh = null;
};

/**
 * Change the tesselator state.
 * @private
 * @param {libtess.GluTesselator.tessState_} state
 */
libtess.GluTesselator.prototype.requireState_ = function(state) {
  if (this.state_ !== state) {
    this.gotoState_(state);
  }
};

/**
 * Change the current tesselator state one level at a time to get to the
 * desired state. Only triggered when the API is not called in the correct order
 * so an error callback is made, however the tesselator will always attempt to
 * recover afterwards (see README).
 * @private
 * @param {libtess.GluTesselator.tessState_} newState
 */
libtess.GluTesselator.prototype.gotoState_ = function(newState) {
  while (this.state_ !== newState) {
    if (this.state_ < newState) {
      switch (this.state_) {
        case libtess.GluTesselator.tessState_.T_DORMANT:
          this.callErrorCallback(
              libtess.errorType.GLU_TESS_MISSING_BEGIN_POLYGON);
          this.gluTessBeginPolygon(null);
          break;

        case libtess.GluTesselator.tessState_.T_IN_POLYGON:
          this.callErrorCallback(
              libtess.errorType.GLU_TESS_MISSING_BEGIN_CONTOUR);
          this.gluTessBeginContour();
          break;
      }

    } else {
      switch (this.state_) {
        case libtess.GluTesselator.tessState_.T_IN_CONTOUR:
          this.callErrorCallback(
              libtess.errorType.GLU_TESS_MISSING_END_CONTOUR);
          this.gluTessEndContour();
          break;

        case libtess.GluTesselator.tessState_.T_IN_POLYGON:
          this.callErrorCallback(
              libtess.errorType.GLU_TESS_MISSING_END_POLYGON);
          // NOTE(bckenny): libtess originally reset the tesselator, even though
          // the README claims it should spit out the tessellated results at
          // this point.
          // (see http://cgit.freedesktop.org/mesa/glu/tree/src/libtess/tess.c#n180)
          this.gluTessEndPolygon();
          break;
      }
    }
  }
};

/**
 * [addVertex_ description]
 * @private
 * @param {!Array<number>} coords [description].
 * @param {Object} data [description].
 */
libtess.GluTesselator.prototype.addVertex_ = function(coords, data) {
  var e = this.lastEdge_;
  if (e === null) {
    // Make a self-loop (one vertex, one edge).
    e = libtess.mesh.makeEdge(this.mesh);
    libtess.mesh.meshSplice(e, e.sym);

  } else {
    // Create a new vertex and edge which immediately follow e
    // in the ordering around the left face.
    libtess.mesh.splitEdge(e);
    e = e.lNext;
  }

  // The new vertex is now e.org.
  e.org.data = data;
  e.org.coords[0] = coords[0];
  e.org.coords[1] = coords[1];
  e.org.coords[2] = coords[2];

  // The winding of an edge says how the winding number changes as we
  // cross from the edge''s right face to its left face.  We add the
  // vertices in such an order that a CCW contour will add +1 to
  // the winding number of the region inside the contour.
  e.winding = 1;
  e.sym.winding = -1;

  this.lastEdge_ = e;
};

/**
 * Call callback to indicate the start of a primitive, to be followed by emitted
 * vertices, if any. In libtess.js, `type` will always be `GL_TRIANGLES`.
 * @param {libtess.primitiveType} type
 */
libtess.GluTesselator.prototype.callBeginCallback = function(type) {
  if (this.beginCallback_) {
    this.beginCallback_(type, this.polygonData_);
  }
};

/**
 * Call callback to emit a vertex of the tessellated polygon.
 * @param {Object} data
 */
libtess.GluTesselator.prototype.callVertexCallback = function(data) {
  if (this.vertexCallback_) {
    this.vertexCallback_(data, this.polygonData_);
  }
};

/**
 * Call callback to indicate whether the vertices to follow begin edges which
 * lie on a polygon boundary.
 * @param {boolean} flag
 */
libtess.GluTesselator.prototype.callEdgeFlagCallback = function(flag) {
  if (this.edgeFlagCallback_) {
    this.edgeFlagCallback_(flag, this.polygonData_);
  }
};

/**
 * Call callback to indicate the end of tessellation.
 */
libtess.GluTesselator.prototype.callEndCallback = function() {
  if (this.endCallback_) {
    this.endCallback_(this.polygonData_);
  }
};

/* jscs:disable maximumLineLength */
/**
 * Call callback for combining vertices at edge intersection requiring the
 * creation of a new vertex.
 * @param {!Array<number>} coords Intersection coordinates.
 * @param {!Array<Object>} data Array of vertex data, one per edge vertices.
 * @param {!Array<number>} weight Coefficients used for the linear combination of vertex coordinates that gives coords.
 * @return {?Object} Interpolated vertex.
 */
libtess.GluTesselator.prototype.callCombineCallback = function(coords, data, weight) {
  if (this.combineCallback_) {
    return this.combineCallback_(coords, data, weight, this.polygonData_) ||
        null;
  }

  return null;
};
/* jscs:enable maximumLineLength */

/**
 * Call error callback, if specified, with errno.
 * @param {(libtess.errorType|libtess.gluEnum)} errno
 */
libtess.GluTesselator.prototype.callErrorCallback = function(errno) {
  if (this.errorCallback_) {
    this.errorCallback_(errno, this.polygonData_);
  }
};

/* global libtess */

/**
 * Each face has a pointer to the next and previous faces in the
 * circular list, and a pointer to a half-edge with this face as
 * the left face (null if this is the dummy header). There is also
 * a field "data" for client data.
 *
 * @param {libtess.GluFace=} opt_nextFace
 * @param {libtess.GluFace=} opt_prevFace
 * @constructor
 * @struct
 */
libtess.GluFace = function(opt_nextFace, opt_prevFace) {
  // TODO(bckenny): reverse order of params?

  /**
   * next face (never null)
   * @type {!libtess.GluFace}
   */
  this.next = opt_nextFace || this;

  /**
   * previous face (never NULL)
   * @type {!libtess.GluFace}
   */
  this.prev = opt_prevFace || this;

  /**
   * A half edge with this left face.
   * @type {libtess.GluHalfEdge}
   */
  this.anEdge = null;

  /**
   * room for client's data
   * @type {Object}
   */
  this.data = null;

  /**
   * This face is in the polygon interior.
   * @type {boolean}
   */
  this.inside = false;
};

/* global libtess */

/**
 * The fundamental data structure is the "half-edge". Two half-edges
 * go together to make an edge, but they point in opposite directions.
 * Each half-edge has a pointer to its mate (the "symmetric" half-edge sym),
 * its origin vertex (org), the face on its left side (lFace), and the
 * adjacent half-edges in the CCW direction around the origin vertex
 * (oNext) and around the left face (lNext). There is also a "next"
 * pointer for the global edge list (see below).
 *
 * The notation used for mesh navigation:
 *  sym   = the mate of a half-edge (same edge, but opposite direction)
 *  oNext = edge CCW around origin vertex (keep same origin)
 *  dNext = edge CCW around destination vertex (keep same dest)
 *  lNext = edge CCW around left face (dest becomes new origin)
 *  rNext = edge CCW around right face (origin becomes new dest)
 *
 * "prev" means to substitute CW for CCW in the definitions above.
 *
 * The circular edge list is special; since half-edges always occur
 * in pairs (e and e.sym), each half-edge stores a pointer in only
 * one direction. Starting at eHead and following the e.next pointers
 * will visit each *edge* once (ie. e or e.sym, but not both).
 * e.sym stores a pointer in the opposite direction, thus it is
 * always true that e.sym.next.sym.next === e.
 *
 * @param {libtess.GluHalfEdge=} opt_nextEdge
 * @constructor
 * @struct
 */
libtess.GluHalfEdge = function(opt_nextEdge) {
  // TODO(bckenny): are these the right defaults? (from gl_meshNewMesh requirements)

  /**
   * doubly-linked list (prev==sym->next)
   * @type {!libtess.GluHalfEdge}
   */
  this.next = opt_nextEdge || this;

  // TODO(bckenny): how can this be required if created in pairs? move to factory creation only?
  /**
   * same edge, opposite direction
   * @type {libtess.GluHalfEdge}
   */
  this.sym = null;

  /**
   * next edge CCW around origin
   * @type {libtess.GluHalfEdge}
   */
  this.oNext = null;

  /**
   * next edge CCW around left face
   * @type {libtess.GluHalfEdge}
   */
  this.lNext = null;

  /**
   * origin vertex (oVertex too long)
   * @type {libtess.GluVertex}
   */
  this.org = null;

  /**
   * left face
   * @type {libtess.GluFace}
   */
  this.lFace = null;

  // Internal data (keep hidden)
  // NOTE(bckenny): can't be private, though...

  /**
   * a region with this upper edge (see sweep.js)
   * @type {libtess.ActiveRegion}
   */
  this.activeRegion = null;

  /**
   * change in winding number when crossing from the right face to the left face
   * @type {number}
   */
  this.winding = 0;
};

// NOTE(bckenny): the following came from macros in mesh
// TODO(bckenny): using methods as aliases for sym connections for now.
// not sure about this approach. getters? renames?


/**
 * [rFace description]
 * @return {libtess.GluFace} [description].
 */
libtess.GluHalfEdge.prototype.rFace = function() {
  return this.sym.lFace;
};


/**
 * [dst description]
 * @return {libtess.GluVertex} [description].
 */
libtess.GluHalfEdge.prototype.dst = function() {
  return this.sym.org;
};


/**
 * [oPrev description]
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.GluHalfEdge.prototype.oPrev = function() {
  return this.sym.lNext;
};


/**
 * [lPrev description]
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.GluHalfEdge.prototype.lPrev = function() {
  return this.oNext.sym;
};

// NOTE(bckenny): libtess.GluHalfEdge.dPrev is called nowhere in libtess and
// isn't part of the current public API. It could be useful for mesh traversal
// and manipulation if made public, however.
/* istanbul ignore next */
/**
 * The edge clockwise around destination vertex (keep same dest).
 * @return {libtess.GluHalfEdge}
 */
libtess.GluHalfEdge.prototype.dPrev = function() {
  return this.lNext.sym;
};


/**
 * [rPrev description]
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.GluHalfEdge.prototype.rPrev = function() {
  return this.sym.oNext;
};


/**
 * [dNext description]
 * @return {libtess.GluHalfEdge} [description].
 */
libtess.GluHalfEdge.prototype.dNext = function() {
  return this.rPrev().sym;
};


// NOTE(bckenny): libtess.GluHalfEdge.rNext is called nowhere in libtess and
// isn't part of the current public API. It could be useful for mesh traversal
// and manipulation if made public, however.
/* istanbul ignore next */
/**
 * The edge CCW around the right face (origin of this becomes new dest).
 * @return {libtess.GluHalfEdge}
 */
libtess.GluHalfEdge.prototype.rNext = function() {
  return this.oPrev().sym;
};

/* global libtess */

/**
 * Creates a new mesh with no edges, no vertices,
 * and no loops (what we usually call a "face").
 *
 * @constructor
 * @struct
 */
libtess.GluMesh = function() {
  /**
   * dummy header for vertex list
   * @type {libtess.GluVertex}
   */
  this.vHead = new libtess.GluVertex();

  /**
   * dummy header for face list
   * @type {libtess.GluFace}
   */
  this.fHead = new libtess.GluFace();

  /**
   * dummy header for edge list
   * @type {libtess.GluHalfEdge}
   */
  this.eHead = new libtess.GluHalfEdge();

  /**
   * and its symmetric counterpart
   * @type {libtess.GluHalfEdge}
   */
  this.eHeadSym = new libtess.GluHalfEdge();

  // TODO(bckenny): better way to pair these?
  this.eHead.sym = this.eHeadSym;
  this.eHeadSym.sym = this.eHead;
};


// TODO(bckenny): #ifndef NDEBUG
/**
 * Checks mesh for self-consistency.
 */
libtess.GluMesh.prototype.checkMesh = function() {
  if (!libtess.DEBUG) {
    return;
  }

  var fHead = this.fHead;
  var vHead = this.vHead;
  var eHead = this.eHead;

  var e;

  // faces
  var f;
  var fPrev = fHead;
  for (fPrev = fHead; (f = fPrev.next) !== fHead; fPrev = f) {
    e = f.anEdge;
    do {
      e = e.lNext;
    } while (e !== f.anEdge);
  }

  // vertices
  var v;
  var vPrev = vHead;
  for (vPrev = vHead; (v = vPrev.next) !== vHead; vPrev = v) {
    e = v.anEdge;
    do {
      e = e.oNext;
    } while (e !== v.anEdge);
  }

  // edges
  var ePrev = eHead;
  for (ePrev = eHead; (e = ePrev.next) !== eHead; ePrev = e) {
  }
};

/* global libtess */

/**
 * Each vertex has a pointer to next and previous vertices in the
 * circular list, and a pointer to a half-edge with this vertex as
 * the origin (null if this is the dummy header). There is also a
 * field "data" for client data.
 * @param {libtess.GluVertex=} opt_nextVertex Optional reference to next vertex in the vertex list.
 * @param {libtess.GluVertex=} opt_prevVertex Optional reference to previous vertex in the vertex list.
 * @constructor
 * @struct
 */
libtess.GluVertex = function(opt_nextVertex, opt_prevVertex) {
  /**
   * Next vertex (never null).
   * @type {!libtess.GluVertex}
   */
  this.next = opt_nextVertex || this;

  /**
   * Previous vertex (never null).
   * @type {!libtess.GluVertex}
   */
  this.prev = opt_prevVertex || this;

  /**
   * A half-edge with this origin.
   * @type {libtess.GluHalfEdge}
   */
  this.anEdge = null;

  /**
   * The client's data.
   * @type {Object}
   */
  this.data = null;

  /**
   * The vertex location in 3D.
   * @type {!Array.<number>}
   */
  this.coords = [0, 0, 0];
  // TODO(bckenny): we may want to rethink coords, either eliminate (using s
  // and t and user data) or index into contiguous storage?

  /**
   * Component of projection onto the sweep plane.
   * @type {number}
   */
  this.s = 0;

  /**
   * Component of projection onto the sweep plane.
   * @type {number}
   */
  this.t = 0;

  /**
   * Handle to allow deletion from priority queue, or 0 if not yet inserted into
   * queue.
   * @type {libtess.PQHandle}
   */
  this.pqHandle = 0;
};

/* global libtess */

/**
 * A priority queue of vertices, ordered by libtess.geom.vertLeq, implemented
 * with a sorted array. Used for initial insertion of vertices (see
 * libtess.sweep.initPriorityQ_), sorted once, then it uses an internal
 * libtess.PriorityQHeap for any subsequently created vertices from
 * intersections.
 * @constructor
 * @struct
 */
libtess.PriorityQ = function() {
  /**
   * An unordered list of vertices that have been inserted in the queue, with
   * null in empty slots.
   * @private {Array<libtess.GluVertex>}
   */
  this.verts_ = [];

  /**
   * Array of indices into this.verts_, sorted by vertLeq over the addressed
   * vertices.
   * @private {Array<number>}
   */
  this.order_ = null;

  /**
   * The size of this queue, not counting any vertices stored in heap_.
   * @private {number}
   */
  this.size_ = 0;

  /**
   * Indicates that the queue has been initialized via init. If false, inserts
   * are fast insertions at the end of the verts_ array. If true, the verts_
   * array is sorted and subsequent inserts are done in the heap.
   * @private {boolean}
   */
  this.initialized_ = false;

  /**
   * A priority queue heap, used for faster insertions of vertices after verts_
   * has been sorted.
   * @private {libtess.PriorityQHeap}
   */
  this.heap_ = new libtess.PriorityQHeap();
};

/**
 * Release major storage memory used by priority queue.
 */
libtess.PriorityQ.prototype.deleteQ = function() {
  // TODO(bckenny): could instead clear most of these.
  this.heap_ = null;
  this.order_ = null;
  this.verts_ = null;
  // NOTE(bckenny): nulled at callsite (sweep.donePriorityQ_)
};

/**
 * Sort vertices by libtess.geom.vertLeq. Must be called before any method other
 * than insert is called to ensure correctness when removing or querying.
 */
libtess.PriorityQ.prototype.init = function() {
  // TODO(bckenny): reuse. in theory, we don't have to empty this, as access is
  // dictated by this.size_, but array.sort doesn't know that
  this.order_ = [];

  // Create an array of indirect pointers to the verts, so that
  // the handles we have returned are still valid.
  // TODO(bckenny): valid for when? it appears we can just store indexes into
  // verts_, but what did this mean?
  for (var i = 0; i < this.size_; i++) {
    this.order_[i] = i;
  }

  // sort the indirect pointers in descending order of the verts themselves
  // TODO(bckenny): make sure it's ok that verts[a] === verts[b] returns 1
  // TODO(bckenny): unstable sort means we may get slightly different polys in
  // different browsers, but only when passing in equal points
  // TODO(bckenny): make less awkward closure?
  var comparator = (function(verts) {
    return function(a, b) {
      return libtess.geom.vertLeq(verts[a], verts[b]) ? 1 : -1;
    };
  })(this.verts_);
  this.order_.sort(comparator);

  this.initialized_ = true;
  this.heap_.init();

  // NOTE(bckenny): debug assert of ordering of the verts_ array.
  if (libtess.DEBUG) {
    var p = 0;
    var r = p + this.size_ - 1;
    for (i = p; i < r; ++i) {
    }
  }
};

/**
 * Insert a vertex into the priority queue. Returns a PQHandle to refer to it,
 * which will never be 0.
 * @param {libtess.GluVertex} vert
 * @return {libtess.PQHandle}
 */
libtess.PriorityQ.prototype.insert = function(vert) {
  // NOTE(bckenny): originally returned LONG_MAX as alloc failure signal. no
  // longer does.
  if (this.initialized_) {
    return this.heap_.insert(vert);
  }

  var curr = this.size_++;

  this.verts_[curr] = vert;

  // Negative handles index the sorted array.
  return -(curr + 1);
};

/**
 * Removes the minimum vertex from the queue and returns it. If the queue is
 * empty, null will be returned.
 * @return {libtess.GluVertex}
 */
libtess.PriorityQ.prototype.extractMin = function() {
  if (this.size_ === 0) {
    return this.heap_.extractMin();
  }

  var sortMin = this.verts_[this.order_[this.size_ - 1]];
  if (!this.heap_.isEmpty()) {
    var heapMin = this.heap_.minimum();
    if (libtess.geom.vertLeq(heapMin, sortMin)) {
      return this.heap_.extractMin();
    }
  }

  do {
    --this.size_;
  } while (this.size_ > 0 && this.verts_[this.order_[this.size_ - 1]] === null);

  return sortMin;
};

/**
 * Returns the minimum vertex in the queue. If the queue is empty, null will be
 * returned.
 * @return {libtess.GluVertex}
 */
libtess.PriorityQ.prototype.minimum = function() {
  if (this.size_ === 0) {
    return this.heap_.minimum();
  }

  var sortMin = this.verts_[this.order_[this.size_ - 1]];
  if (!this.heap_.isEmpty()) {
    var heapMin = this.heap_.minimum();
    if (libtess.geom.vertLeq(heapMin, sortMin)) {
      return heapMin;
    }
  }

  return sortMin;
};

/**
 * Remove vertex with handle removeHandle from queue.
 * @param {libtess.PQHandle} removeHandle
 */
libtess.PriorityQ.prototype.remove = function(removeHandle) {
  if (removeHandle >= 0) {
    this.heap_.remove(removeHandle);
    return;
  }
  removeHandle = -(removeHandle + 1);

  this.verts_[removeHandle] = null;
  while (this.size_ > 0 && this.verts_[this.order_[this.size_ - 1]] === null) {
    --this.size_;
  }
};

/* global libtess */

/**
 * A priority queue of vertices, ordered by libtess.geom.vertLeq, implemented
 * with a binary heap. Used only within libtess.PriorityQ for prioritizing
 * vertices created by intersections (see libtess.sweep.checkForIntersect_).
 * @constructor
 * @struct
 */
libtess.PriorityQHeap = function() {
  /**
   * The heap itself. Active nodes are stored in the range 1..size, with the
   * minimum at 1. Each node stores only an index into verts_ and handles_.
   * @private {!Array<number>}
   */
  this.heap_ = libtess.PriorityQHeap.reallocNumeric_([0],
      libtess.PriorityQHeap.INIT_SIZE_ + 1);

  /**
   * An unordered list of vertices in the heap, with null in empty slots.
   * @private {!Array<libtess.GluVertex>}
   */
  this.verts_ = [null, null];

  /**
   * An unordered list of indices mapping vertex handles into the heap. An entry
   * at index i will map the vertex at i in verts_ to its place in the heap
   * (i.e. heap_[handles_[i]] === i).
   * Empty slots below size_ are a free list chain starting at freeList_.
   * @private {!Array<number>}
   */
  this.handles_ = [0, 0];

  /**
   * The size of the queue.
   * @private {number}
   */
  this.size_ = 0;

  /**
   * The queue's current allocated space.
   * @private {number}
   */
  this.max_ = libtess.PriorityQHeap.INIT_SIZE_;

  /**
   * The index of the next free hole in the verts_ array. That slot in handles_
   * has the next index in the free list. If there are no holes, freeList_ === 0
   * and a new vertex must be appended to the list.
   * @private {libtess.PQHandle}
   */
  this.freeList_ = 0;

  /**
   * Indicates that the heap has been initialized via init. If false, inserts
   * are fast insertions at the end of a list. If true, all inserts will now be
   * correctly ordered in the queue before returning.
   * @private {boolean}
   */
  this.initialized_ = false;

  // Point the first index at the first (currently null) vertex.
  this.heap_[1] = 1;
};

/**
 * The initial allocated space for the queue.
 * @const
 * @private {number}
 */
libtess.PriorityQHeap.INIT_SIZE_ = 32;

/**
 * Allocate a numeric index array of size size. oldArray's contents are copied
 * to the beginning of the new array. The rest of the array is filled with
 * zeroes.
 * @private
 * @param {!Array<number>} oldArray
 * @param {number} size
 * @return {!Array<number>}
 */
libtess.PriorityQHeap.reallocNumeric_ = function(oldArray, size) {
  var newArray = new Array(size);

  // NOTE(bckenny): V8 likes this significantly more than simply growing the
  // array element-by-element or expanding the existing array all at once, so,
  // for now, emulating realloc.
  for (var index = 0; index < oldArray.length; index++) {
    newArray[index] = oldArray[index];
  }

  for (; index < size; index++) {
    newArray[index] = 0;
  }

  return newArray;
};

/**
 * Initializing ordering of the heap. Must be called before any method other
 * than insert is called to ensure correctness when removing or querying.
 */
libtess.PriorityQHeap.prototype.init = function() {
  // This method of building a heap is O(n), rather than O(n lg n).
  for (var i = this.size_; i >= 1; --i) {
    // TODO(bckenny): since init is called before anything is inserted (see
    // PriorityQ.init), this will always be empty. Better to lazily init?
    this.floatDown_(i);
  }

  this.initialized_ = true;
};

/**
 * Insert a new vertex into the heap.
 * @param {libtess.GluVertex} vert The vertex to insert.
 * @return {libtess.PQHandle} A handle that can be used to remove the vertex.
 */
libtess.PriorityQHeap.prototype.insert = function(vert) {
  var endIndex = ++this.size_;

  // If the heap overflows, double its size.
  if ((endIndex * 2) > this.max_) {
    this.max_ *= 2;

    this.handles_ = libtess.PriorityQHeap.reallocNumeric_(this.handles_,
        this.max_ + 1);
  }

  var newVertSlot;
  if (this.freeList_ === 0) {
    // No free slots, append vertex.
    newVertSlot = endIndex;
  } else {
    // Put vertex in free slot, update freeList_ to next free slot.
    newVertSlot = this.freeList_;
    this.freeList_ = this.handles_[this.freeList_];
  }

  this.verts_[newVertSlot] = vert;
  this.handles_[newVertSlot] = endIndex;
  this.heap_[endIndex] = newVertSlot;

  if (this.initialized_) {
    this.floatUp_(endIndex);
  }
  return newVertSlot;
};

/**
 * @return {boolean} Whether the heap is empty.
 */
libtess.PriorityQHeap.prototype.isEmpty = function() {
  return this.size_ === 0;
};

/**
 * Returns the minimum vertex in the heap. If the heap is empty, null will be
 * returned.
 * @return {libtess.GluVertex}
 */
libtess.PriorityQHeap.prototype.minimum = function() {
  return this.verts_[this.heap_[1]];
};

/**
 * Removes the minimum vertex from the heap and returns it. If the heap is
 * empty, null will be returned.
 * @return {libtess.GluVertex}
 */
libtess.PriorityQHeap.prototype.extractMin = function() {
  var heap = this.heap_;
  var verts = this.verts_;
  var handles = this.handles_;

  var minHandle = heap[1];
  var minVertex = verts[minHandle];

  if (this.size_ > 0) {
    // Replace min with last vertex.
    heap[1] = heap[this.size_];
    handles[heap[1]] = 1;

    // Clear min vertex and put slot at front of freeList_.
    verts[minHandle] = null;
    handles[minHandle] = this.freeList_;
    this.freeList_ = minHandle;

    // Restore heap.
    if (--this.size_ > 0) {
      this.floatDown_(1);
    }
  }

  return minVertex;
};

/**
 * Remove vertex with handle removeHandle from heap.
 * @param {libtess.PQHandle} removeHandle
 */
libtess.PriorityQHeap.prototype.remove = function(removeHandle) {
  var heap = this.heap_;
  var verts = this.verts_;
  var handles = this.handles_;

  var heapIndex = handles[removeHandle];

  // Replace with last vertex.
  heap[heapIndex] = heap[this.size_];
  handles[heap[heapIndex]] = heapIndex;

  // Restore heap.
  if (heapIndex <= --this.size_) {
    if (heapIndex <= 1) {
      this.floatDown_(heapIndex);
    } else {
      var vert = verts[heap[heapIndex]];
      var parentVert = verts[heap[heapIndex >> 1]];
      if (libtess.geom.vertLeq(parentVert, vert)) {
        this.floatDown_(heapIndex);
      } else {
        this.floatUp_(heapIndex);
      }
    }
  }

  // Clear vertex and put slot at front of freeList_.
  verts[removeHandle] = null;
  handles[removeHandle] = this.freeList_;
  this.freeList_ = removeHandle;
};

/**
 * Restore heap by moving the vertex at index in the heap downwards to a valid
 * slot.
 * @private
 * @param {libtess.PQHandle} index
 */
libtess.PriorityQHeap.prototype.floatDown_ = function(index) {
  var heap = this.heap_;
  var verts = this.verts_;
  var handles = this.handles_;

  var currIndex = index;
  var currHandle = heap[currIndex];
  for (;;) {
    // The children of node i are nodes 2i and 2i+1.
    var childIndex = currIndex << 1;
    if (childIndex < this.size_) {
      // Set child to the index of the child with the minimum vertex.
      if (libtess.geom.vertLeq(verts[heap[childIndex + 1]],
          verts[heap[childIndex]])) {
        childIndex = childIndex + 1;
      }
    }

    var childHandle = heap[childIndex];
    if (childIndex > this.size_ ||
        libtess.geom.vertLeq(verts[currHandle], verts[childHandle])) {
      // Heap restored.
      heap[currIndex] = currHandle;
      handles[currHandle] = currIndex;
      return;
    }

    // Swap current node and child; repeat from childIndex.
    heap[currIndex] = childHandle;
    handles[childHandle] = currIndex;
    currIndex = childIndex;
  }
};

/**
 * Restore heap by moving the vertex at index in the heap upwards to a valid
 * slot.
 * @private
 * @param {libtess.PQHandle} index
 */
libtess.PriorityQHeap.prototype.floatUp_ = function(index) {
  var heap = this.heap_;
  var verts = this.verts_;
  var handles = this.handles_;

  var currIndex = index;
  var currHandle = heap[currIndex];
  for (;;) {
    // The parent of node i is node floor(i/2).
    var parentIndex = currIndex >> 1;
    var parentHandle = heap[parentIndex];

    if (parentIndex === 0 ||
        libtess.geom.vertLeq(verts[parentHandle], verts[currHandle])) {
      // Heap restored.
      heap[currIndex] = currHandle;
      handles[currHandle] = currIndex;
      return;
    }

    // Swap current node and parent; repeat from parentIndex.
    heap[currIndex] = parentHandle;
    handles[parentHandle] = currIndex;
    currIndex = parentIndex;
  }
};

/* global libtess */

// TODO(bckenny): apparently only visible outside of sweep for debugging routines.
// find out if we can hide

/**
 * For each pair of adjacent edges crossing the sweep line, there is
 * an ActiveRegion to represent the region between them. The active
 * regions are kept in sorted order in a dynamic dictionary. As the
 * sweep line crosses each vertex, we update the affected regions.
 * @constructor
 * @struct
 */
libtess.ActiveRegion = function() {
  // TODO(bckenny): I *think* eUp and nodeUp could be passed in as constructor params

  /**
   * The upper edge of the region, directed right to left
   * @type {libtess.GluHalfEdge}
   */
  this.eUp = null;

  /**
   * Dictionary node corresponding to eUp edge.
   * @type {libtess.DictNode}
   */
  this.nodeUp = null;

  /**
   * Used to determine which regions are inside the polygon.
   * @type {number}
   */
  this.windingNumber = 0;

  /**
   * Whether this region is inside the polygon.
   * @type {boolean}
   */
  this.inside = false;

  /**
   * Marks fake edges at t = +/-infinity.
   * @type {boolean}
   */
  this.sentinel = false;

  /**
   * Marks regions where the upper or lower edge has changed, but we haven't
   * checked whether they intersect yet.
   * @type {boolean}
   */
  this.dirty = false;

  /**
   * marks temporary edges introduced when we process a "right vertex" (one
   * without any edges leaving to the right)
   * @type {boolean}
   */
  this.fixUpperEdge = false;
};

/**
 * Returns the ActiveRegion below this one.
 * @return {libtess.ActiveRegion}
 */
libtess.ActiveRegion.prototype.regionBelow = function() {
  return this.nodeUp.getPredecessor().getKey();
};

/**
 * Returns the ActiveRegion above this one.
 * @return {libtess.ActiveRegion}
 */
libtess.ActiveRegion.prototype.regionAbove = function() {
  return this.nodeUp.getSuccessor().getKey();
};

/* global libtess, module */

/**
 * node.js export for non-compiled source
 */
if (true) {
  module.exports = libtess;
}


/***/ },

/***/ 226
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

const Namespace = { };

Object .defineProperty (Namespace, "add",
{
   value (name, module)
   {
      if (Namespace .hasOwnProperty (name))
      {
         console .error (new Error (`Namespace: "${name}" already exists.`));
         return module;
      }

      const X3D = window [Symbol .for ("X_ITE.X3D")];

      if (X3D)
         X3D [name] = module;

      // For x_ite.js:
      return Namespace [name] = module;
   },
});

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Namespace .add ("Namespace", Namespace));

/* harmony export */ __webpack_require__.d(__webpack_exports__, [
/* harmony export */   "A", 0, /* export default binding */ __WEBPACK_DEFAULT_EXPORT__
/* harmony export */ ]);


/***/ },

/***/ 228
(module, __unused_webpack_exports, __webpack_require__) {



// Bundlers are able to synchronously require an ESM module from a CommonJS one.
const { jQuery } = __webpack_require__( 44 );
module.exports = jQuery;


/***/ },

/***/ 44
(__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   jQuery: () => (/* binding */ jQuery)
/* harmony export */ });
/* unused harmony export $ */
/* harmony import */ var _src_x_ite_Namespace_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(226);
/*!
 * jQuery JavaScript Library v4.0.0+slim
 * https://jquery.com/
 *
 * Copyright OpenJS Foundation and other contributors
 * Released under the MIT license
 * https://jquery.com/license/
 *
 * Date: 2026-01-18T00:20Z
 */
// For ECMAScript module environments where a proper `window`
// is present, execute the factory and get jQuery.
function jQueryFactory( window, noGlobal ) {

if ( typeof window === "undefined" || !window.document ) {
	throw new Error( "jQuery requires a window with a document" );
}

var arr = [];

var getProto = Object.getPrototypeOf;

var slice = arr.slice;

// Support: IE 11+
// IE doesn't have Array#flat; provide a fallback.
var flat = arr.flat ? function( array ) {
	return arr.flat.call( array );
} : function( array ) {
	return arr.concat.apply( [], array );
};

var push = arr.push;

var indexOf = arr.indexOf;

// [[Class]] -> type pairs
var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

var ObjectFunctionString = fnToString.call( Object );

// All support tests are defined in their respective modules.
var support = {};

function toType( obj ) {
	if ( obj == null ) {
		return obj + "";
	}

	return typeof obj === "object" ?
		class2type[ toString.call( obj ) ] || "object" :
		typeof obj;
}

function isWindow( obj ) {
	return obj != null && obj === obj.window;
}

function isArrayLike( obj ) {

	var length = !!obj && obj.length,
		type = toType( obj );

	if ( typeof obj === "function" || isWindow( obj ) ) {
		return false;
	}

	return type === "array" || length === 0 ||
		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}

var document$1 = window.document;

var preservedScriptAttributes = {
	type: true,
	src: true,
	nonce: true,
	noModule: true
};

function DOMEval( code, node, doc ) {
	doc = doc || document$1;

	var i,
		script = doc.createElement( "script" );

	script.text = code;
	for ( i in preservedScriptAttributes ) {
		if ( node && node[ i ] ) {
			script[ i ] = node[ i ];
		}
	}

	if ( doc.head.appendChild( script ).parentNode ) {
		script.parentNode.removeChild( script );
	}
}

var version = "4.0.0+slim",

	rhtmlSuffix = /HTML$/i,

	// Define a local copy of jQuery
	jQuery = function( selector, context ) {

		// The jQuery object is actually just the init constructor 'enhanced'
		// Need init if jQuery is called (just allow error to be thrown if not included)
		return new jQuery.fn.init( selector, context );
	};

jQuery.fn = jQuery.prototype = {

	// The current version of jQuery being used
	jquery: version,

	constructor: jQuery,

	// The default length of a jQuery object is 0
	length: 0,

	toArray: function() {
		return slice.call( this );
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {

		// Return all the elements in a clean array
		if ( num == null ) {
			return slice.call( this );
		}

		// Return just the one element from the set
		return num < 0 ? this[ num + this.length ] : this[ num ];
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems ) {

		// Build a new jQuery matched element set
		var ret = jQuery.merge( this.constructor(), elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;

		// Return the newly-formed element set
		return ret;
	},

	// Execute a callback for every element in the matched set.
	each: function( callback ) {
		return jQuery.each( this, callback );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map( this, function( elem, i ) {
			return callback.call( elem, i, elem );
		} ) );
	},

	slice: function() {
		return this.pushStack( slice.apply( this, arguments ) );
	},

	first: function() {
		return this.eq( 0 );
	},

	last: function() {
		return this.eq( -1 );
	},

	even: function() {
		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
			return ( i + 1 ) % 2;
		} ) );
	},

	odd: function() {
		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
			return i % 2;
		} ) );
	},

	eq: function( i ) {
		var len = this.length,
			j = +i + ( i < 0 ? len : 0 );
		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
	},

	end: function() {
		return this.prevObject || this.constructor();
	}
};

jQuery.extend = jQuery.fn.extend = function() {
	var options, name, src, copy, copyIsArray, clone,
		target = arguments[ 0 ] || {},
		i = 1,
		length = arguments.length,
		deep = false;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;

		// Skip the boolean and the target
		target = arguments[ i ] || {};
		i++;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && typeof target !== "function" ) {
		target = {};
	}

	// Extend jQuery itself if only one argument is passed
	if ( i === length ) {
		target = this;
		i--;
	}

	for ( ; i < length; i++ ) {

		// Only deal with non-null/undefined values
		if ( ( options = arguments[ i ] ) != null ) {

			// Extend the base object
			for ( name in options ) {
				copy = options[ name ];

				// Prevent Object.prototype pollution
				// Prevent never-ending loop
				if ( name === "__proto__" || target === copy ) {
					continue;
				}

				// Recurse if we're merging plain objects or arrays
				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
					( copyIsArray = Array.isArray( copy ) ) ) ) {
					src = target[ name ];

					// Ensure proper type for the source value
					if ( copyIsArray && !Array.isArray( src ) ) {
						clone = [];
					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
						clone = {};
					} else {
						clone = src;
					}
					copyIsArray = false;

					// Never move original objects, clone them
					target[ name ] = jQuery.extend( deep, clone, copy );

				// Don't bring in undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
};

jQuery.extend( {

	// Unique for each copy of jQuery on the page
	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),

	// Assume jQuery is ready without the ready module
	isReady: true,

	error: function( msg ) {
		throw new Error( msg );
	},

	noop: function() {},

	isPlainObject: function( obj ) {
		var proto, Ctor;

		// Detect obvious negatives
		// Use toString instead of jQuery.type to catch host objects
		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
			return false;
		}

		proto = getProto( obj );

		// Objects with no prototype (e.g., `Object.create( null )`) are plain
		if ( !proto ) {
			return true;
		}

		// Objects with prototype are plain iff they were constructed by a global Object function
		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
	},

	isEmptyObject: function( obj ) {
		var name;

		for ( name in obj ) {
			return false;
		}
		return true;
	},

	// Evaluates a script in a provided context; falls back to the global one
	// if not specified.
	globalEval: function( code, options, doc ) {
		DOMEval( code, { nonce: options && options.nonce }, doc );
	},

	each: function( obj, callback ) {
		var length, i = 0;

		if ( isArrayLike( obj ) ) {
			length = obj.length;
			for ( ; i < length; i++ ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		} else {
			for ( i in obj ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		}

		return obj;
	},


	// Retrieve the text value of an array of DOM nodes
	text: function( elem ) {
		var node,
			ret = "",
			i = 0,
			nodeType = elem.nodeType;

		if ( !nodeType ) {

			// If no nodeType, this is expected to be an array
			while ( ( node = elem[ i++ ] ) ) {

				// Do not traverse comment nodes
				ret += jQuery.text( node );
			}
		}
		if ( nodeType === 1 || nodeType === 11 ) {
			return elem.textContent;
		}
		if ( nodeType === 9 ) {
			return elem.documentElement.textContent;
		}
		if ( nodeType === 3 || nodeType === 4 ) {
			return elem.nodeValue;
		}

		// Do not include comment or processing instruction nodes

		return ret;
	},


	// results is for internal usage only
	makeArray: function( arr, results ) {
		var ret = results || [];

		if ( arr != null ) {
			if ( isArrayLike( Object( arr ) ) ) {
				jQuery.merge( ret,
					typeof arr === "string" ?
						[ arr ] : arr
				);
			} else {
				push.call( ret, arr );
			}
		}

		return ret;
	},

	inArray: function( elem, arr, i ) {
		return arr == null ? -1 : indexOf.call( arr, elem, i );
	},

	isXMLDoc: function( elem ) {
		var namespace = elem && elem.namespaceURI,
			docElem = elem && ( elem.ownerDocument || elem ).documentElement;

		// Assume HTML when documentElement doesn't yet exist, such as inside
		// document fragments.
		return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" );
	},

	// Note: an element does not contain itself
	contains: function( a, b ) {
		var bup = b && b.parentNode;

		return a === bup || !!( bup && bup.nodeType === 1 && (

			// Support: IE 9 - 11+
			// IE doesn't have `contains` on SVG.
			a.contains ?
				a.contains( bup ) :
				a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
		) );
	},

	merge: function( first, second ) {
		var len = +second.length,
			j = 0,
			i = first.length;

		for ( ; j < len; j++ ) {
			first[ i++ ] = second[ j ];
		}

		first.length = i;

		return first;
	},

	grep: function( elems, callback, invert ) {
		var callbackInverse,
			matches = [],
			i = 0,
			length = elems.length,
			callbackExpect = !invert;

		// Go through the array, only saving the items
		// that pass the validator function
		for ( ; i < length; i++ ) {
			callbackInverse = !callback( elems[ i ], i );
			if ( callbackInverse !== callbackExpect ) {
				matches.push( elems[ i ] );
			}
		}

		return matches;
	},

	// arg is for internal usage only
	map: function( elems, callback, arg ) {
		var length, value,
			i = 0,
			ret = [];

		// Go through the array, translating each of the items to their new values
		if ( isArrayLike( elems ) ) {
			length = elems.length;
			for ( ; i < length; i++ ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}

		// Go through every key on the object,
		} else {
			for ( i in elems ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}
		}

		// Flatten any nested arrays
		return flat( ret );
	},

	// A global GUID counter for objects
	guid: 1,

	// jQuery.support is not used in Core but other projects attach their
	// properties to it so it needs to exist.
	support: support
} );

if ( typeof Symbol === "function" ) {
	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}

// Populate the class2type map
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
	function( _i, name ) {
		class2type[ "[object " + name + "]" ] = name.toLowerCase();
	} );

function nodeName( elem, name ) {
	return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
}

var pop = arr.pop;

// https://www.w3.org/TR/css3-selectors/#whitespace
var whitespace = "[\\x20\\t\\r\\n\\f]";

var isIE = document$1.documentMode;

var rbuggyQSA = isIE && new RegExp(

	// Support: IE 9 - 11+
	// IE's :disabled selector does not pick up the children of disabled fieldsets
	":enabled|:disabled|" +

	// Support: IE 11+
	// IE 11 doesn't find elements on a `[name='']` query in some cases.
	// Adding a temporary attribute to the document before the selection works
	// around the issue.
	"\\[" + whitespace + "*name" + whitespace + "*=" +
	whitespace + "*(?:''|\"\")"

);

var rtrimCSS = new RegExp(
	"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
	"g"
);

// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
var identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
	"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+";

var rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" +
	whitespace + ")" + whitespace + "*" );

var rdescend = new RegExp( whitespace + "|>" );

var rsibling = /[+~]/;

var documentElement$1 = document$1.documentElement;

// Support: IE 9 - 11+
// IE requires a prefix.
var matches = documentElement$1.matches || documentElement$1.msMatchesSelector;

/**
 * Create key-value caches of limited size
 * @returns {function(string, object)} Returns the Object data after storing it on itself with
 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
 *	deleting the oldest entry
 */
function createCache() {
	var keys = [];

	function cache( key, value ) {

		// Use (key + " ") to avoid collision with native prototype properties
		// (see https://github.com/jquery/sizzle/issues/157)
		if ( keys.push( key + " " ) > jQuery.expr.cacheLength ) {

			// Only keep the most recent entries
			delete cache[ keys.shift() ];
		}
		return ( cache[ key + " " ] = value );
	}
	return cache;
}

/**
 * Checks a node for validity as a jQuery selector context
 * @param {Element|Object=} context
 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
 */
function testContext( context ) {
	return context && typeof context.getElementsByTagName !== "undefined" && context;
}

// Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors
var attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +

	// Operator (capture 2)
	"*([*^$|!~]?=)" + whitespace +

	// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
	"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
	whitespace + "*\\]";

var pseudos = ":(" + identifier + ")(?:\\((" +

	// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
	// 1. quoted (capture 3; capture 4 or capture 5)
	"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +

	// 2. simple (capture 6)
	"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +

	// 3. anything else (capture 2)
	".*" +
	")\\)|)";

var filterMatchExpr = {
	ID: new RegExp( "^#(" + identifier + ")" ),
	CLASS: new RegExp( "^\\.(" + identifier + ")" ),
	TAG: new RegExp( "^(" + identifier + "|[*])" ),
	ATTR: new RegExp( "^" + attributes ),
	PSEUDO: new RegExp( "^" + pseudos ),
	CHILD: new RegExp(
		"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
		whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
		whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" )
};

var rpseudo = new RegExp( pseudos );

// CSS escapes
// https://www.w3.org/TR/CSS21/syndata.html#escaped-characters

var runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
	"?|\\\\([^\\r\\n\\f])", "g" ),
	funescape = function( escape, nonHex ) {
		var high = "0x" + escape.slice( 1 ) - 0x10000;

		if ( nonHex ) {

			// Strip the backslash prefix from a non-hex escape sequence
			return nonHex;
		}

		// Replace a hexadecimal escape sequence with the encoded Unicode code point
		// Support: IE <=11+
		// For values outside the Basic Multilingual Plane (BMP), manually construct a
		// surrogate pair
		return high < 0 ?
			String.fromCharCode( high + 0x10000 ) :
			String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
	};

function unescapeSelector( sel ) {
	return sel.replace( runescape, funescape );
}

function selectorError( msg ) {
	jQuery.error( "Syntax error, unrecognized expression: " + msg );
}

var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" );

var tokenCache = createCache();

function tokenize( selector, parseOnly ) {
	var matched, match, tokens, type,
		soFar, groups, preFilters,
		cached = tokenCache[ selector + " " ];

	if ( cached ) {
		return parseOnly ? 0 : cached.slice( 0 );
	}

	soFar = selector;
	groups = [];
	preFilters = jQuery.expr.preFilter;

	while ( soFar ) {

		// Comma and first run
		if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
			if ( match ) {

				// Don't consume trailing commas as valid
				soFar = soFar.slice( match[ 0 ].length ) || soFar;
			}
			groups.push( ( tokens = [] ) );
		}

		matched = false;

		// Combinators
		if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
			matched = match.shift();
			tokens.push( {
				value: matched,

				// Cast descendant combinators to space
				type: match[ 0 ].replace( rtrimCSS, " " )
			} );
			soFar = soFar.slice( matched.length );
		}

		// Filters
		for ( type in filterMatchExpr ) {
			if ( ( match = jQuery.expr.match[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
				( match = preFilters[ type ]( match ) ) ) ) {
				matched = match.shift();
				tokens.push( {
					value: matched,
					type: type,
					matches: match
				} );
				soFar = soFar.slice( matched.length );
			}
		}

		if ( !matched ) {
			break;
		}
	}

	// Return the length of the invalid excess
	// if we're just parsing
	// Otherwise, throw an error or return tokens
	if ( parseOnly ) {
		return soFar.length;
	}

	return soFar ?
		selectorError( selector ) :

		// Cache the tokens
		tokenCache( selector, groups ).slice( 0 );
}

var preFilter = {
	ATTR: function( match ) {
		match[ 1 ] = unescapeSelector( match[ 1 ] );

		// Move the given value to match[3] whether quoted or unquoted
		match[ 3 ] = unescapeSelector( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" );

		if ( match[ 2 ] === "~=" ) {
			match[ 3 ] = " " + match[ 3 ] + " ";
		}

		return match.slice( 0, 4 );
	},

	CHILD: function( match ) {

		/* matches from filterMatchExpr["CHILD"]
			1 type (only|nth|...)
			2 what (child|of-type)
			3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
			4 xn-component of xn+y argument ([+-]?\d*n|)
			5 sign of xn-component
			6 x of xn-component
			7 sign of y-component
			8 y of y-component
		*/
		match[ 1 ] = match[ 1 ].toLowerCase();

		if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {

			// nth-* requires argument
			if ( !match[ 3 ] ) {
				selectorError( match[ 0 ] );
			}

			// numeric x and y parameters for jQuery.expr.filter.CHILD
			// remember that false/true cast respectively to 0/1
			match[ 4 ] = +( match[ 4 ] ?
				match[ 5 ] + ( match[ 6 ] || 1 ) :
				2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
			);
			match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );

		// other types prohibit arguments
		} else if ( match[ 3 ] ) {
			selectorError( match[ 0 ] );
		}

		return match;
	},

	PSEUDO: function( match ) {
		var excess,
			unquoted = !match[ 6 ] && match[ 2 ];

		if ( filterMatchExpr.CHILD.test( match[ 0 ] ) ) {
			return null;
		}

		// Accept quoted arguments as-is
		if ( match[ 3 ] ) {
			match[ 2 ] = match[ 4 ] || match[ 5 ] || "";

		// Strip excess characters from unquoted arguments
		} else if ( unquoted && rpseudo.test( unquoted ) &&

			// Get excess from tokenize (recursively)
			( excess = tokenize( unquoted, true ) ) &&

			// advance to the next closing parenthesis
			( excess = unquoted.indexOf( ")", unquoted.length - excess ) -
				unquoted.length ) ) {

			// excess is a negative index
			match[ 0 ] = match[ 0 ].slice( 0, excess );
			match[ 2 ] = unquoted.slice( 0, excess );
		}

		// Return only captures needed by the pseudo filter method (type and argument)
		return match.slice( 0, 3 );
	}
};

function toSelector( tokens ) {
	var i = 0,
		len = tokens.length,
		selector = "";
	for ( ; i < len; i++ ) {
		selector += tokens[ i ].value;
	}
	return selector;
}

// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
function access( elems, fn, key, value, chainable, emptyGet, raw ) {
	var i = 0,
		len = elems.length,
		bulk = key == null;

	// Sets many values
	if ( toType( key ) === "object" ) {
		chainable = true;
		for ( i in key ) {
			access( elems, fn, i, key[ i ], true, emptyGet, raw );
		}

	// Sets one value
	} else if ( value !== undefined ) {
		chainable = true;

		if ( typeof value !== "function" ) {
			raw = true;
		}

		if ( bulk ) {

			// Bulk operations run against the entire set
			if ( raw ) {
				fn.call( elems, value );
				fn = null;

			// ...except when executing function values
			} else {
				bulk = fn;
				fn = function( elem, _key, value ) {
					return bulk.call( jQuery( elem ), value );
				};
			}
		}

		if ( fn ) {
			for ( ; i < len; i++ ) {
				fn(
					elems[ i ], key, raw ?
						value :
						value.call( elems[ i ], i, fn( elems[ i ], key ) )
				);
			}
		}
	}

	if ( chainable ) {
		return elems;
	}

	// Gets
	if ( bulk ) {
		return fn.call( elems );
	}

	return len ? fn( elems[ 0 ], key ) : emptyGet;
}

// Only count HTML whitespace
// Other whitespace should count in values
// https://infra.spec.whatwg.org/#ascii-whitespace
var rnothtmlwhite = /[^\x20\t\r\n\f]+/g;

jQuery.fn.extend( {
	attr: function( name, value ) {
		return access( this, jQuery.attr, name, value, arguments.length > 1 );
	},

	removeAttr: function( name ) {
		return this.each( function() {
			jQuery.removeAttr( this, name );
		} );
	}
} );

jQuery.extend( {
	attr: function( elem, name, value ) {
		var ret, hooks,
			nType = elem.nodeType;

		// Don't get/set attributes on text, comment and attribute nodes
		if ( nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		// Fallback to prop when attributes are not supported
		if ( typeof elem.getAttribute === "undefined" ) {
			return jQuery.prop( elem, name, value );
		}

		// Attribute hooks are determined by the lowercase version
		// Grab necessary hook if one is defined
		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
			hooks = jQuery.attrHooks[ name.toLowerCase() ];
		}

		if ( value !== undefined ) {
			if ( value === null ||

				// For compat with previous handling of boolean attributes,
				// remove when `false` passed. For ARIA attributes -
				// many of which recognize a `"false"` value - continue to
				// set the `"false"` value as jQuery <4 did.
				( value === false && name.toLowerCase().indexOf( "aria-" ) !== 0 ) ) {

				jQuery.removeAttr( elem, name );
				return;
			}

			if ( hooks && "set" in hooks &&
				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
				return ret;
			}

			elem.setAttribute( name, value );
			return value;
		}

		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
			return ret;
		}

		ret = elem.getAttribute( name );

		// Non-existent attributes return null, we normalize to undefined
		return ret == null ? undefined : ret;
	},

	attrHooks: {},

	removeAttr: function( elem, value ) {
		var name,
			i = 0,

			// Attribute names can contain non-HTML whitespace characters
			// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
			attrNames = value && value.match( rnothtmlwhite );

		if ( attrNames && elem.nodeType === 1 ) {
			while ( ( name = attrNames[ i++ ] ) ) {
				elem.removeAttribute( name );
			}
		}
	}
} );

// Support: IE <=11+
// An input loses its value after becoming a radio
if ( isIE ) {
	jQuery.attrHooks.type = {
		set: function( elem, value ) {
			if ( value === "radio" && nodeName( elem, "input" ) ) {
				var val = elem.value;
				elem.setAttribute( "type", value );
				if ( val ) {
					elem.value = val;
				}
				return value;
			}
		}
	};
}

// CSS string/identifier serialization
// https://drafts.csswg.org/cssom/#common-serializing-idioms
var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;

function fcssescape( ch, asCodePoint ) {
	if ( asCodePoint ) {

		// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
		if ( ch === "\0" ) {
			return "\uFFFD";
		}

		// Control characters and (dependent upon position) numbers get escaped as code points
		return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
	}

	// Other potentially-special ASCII characters get backslash-escaped
	return "\\" + ch;
}

jQuery.escapeSelector = function( sel ) {
	return ( sel + "" ).replace( rcssescape, fcssescape );
};

var sort = arr.sort;

var splice = arr.splice;

var hasDuplicate;

// Document order sorting
function sortOrder( a, b ) {

	// Flag for duplicate removal
	if ( a === b ) {
		hasDuplicate = true;
		return 0;
	}

	// Sort on method existence if only one input has compareDocumentPosition
	var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
	if ( compare ) {
		return compare;
	}

	// Calculate position if both inputs belong to the same document
	// Support: IE 11+
	// IE sometimes throws a "Permission denied" error when strict-comparing
	// two documents; shallow comparisons work.
	// eslint-disable-next-line eqeqeq
	compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
		a.compareDocumentPosition( b ) :

		// Otherwise we know they are disconnected
		1;

	// Disconnected nodes
	if ( compare & 1 ) {

		// Choose the first element that is related to the document
		// Support: IE 11+
		// IE sometimes throws a "Permission denied" error when strict-comparing
		// two documents; shallow comparisons work.
		// eslint-disable-next-line eqeqeq
		if ( a == document$1 || a.ownerDocument == document$1 &&
			jQuery.contains( document$1, a ) ) {
			return -1;
		}

		// Support: IE 11+
		// IE sometimes throws a "Permission denied" error when strict-comparing
		// two documents; shallow comparisons work.
		// eslint-disable-next-line eqeqeq
		if ( b == document$1 || b.ownerDocument == document$1 &&
			jQuery.contains( document$1, b ) ) {
			return 1;
		}

		// Maintain original order
		return 0;
	}

	return compare & 4 ? -1 : 1;
}

/**
 * Document sorting and removing duplicates
 * @param {ArrayLike} results
 */
jQuery.uniqueSort = function( results ) {
	var elem,
		duplicates = [],
		j = 0,
		i = 0;

	hasDuplicate = false;

	sort.call( results, sortOrder );

	if ( hasDuplicate ) {
		while ( ( elem = results[ i++ ] ) ) {
			if ( elem === results[ i ] ) {
				j = duplicates.push( i );
			}
		}
		while ( j-- ) {
			splice.call( results, duplicates[ j ], 1 );
		}
	}

	return results;
};

jQuery.fn.uniqueSort = function() {
	return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) );
};

var i,
	outermostContext,

	// Local document vars
	document,
	documentElement,
	documentIsHTML,

	// Instance-specific data
	dirruns = 0,
	done = 0,
	classCache = createCache(),
	compilerCache = createCache(),
	nonnativeSelectorCache = createCache(),

	// Regular expressions

	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
	rwhitespace = new RegExp( whitespace + "+", "g" ),

	ridentifier = new RegExp( "^" + identifier + "$" ),

	matchExpr = jQuery.extend( {

		// For use in libraries implementing .is()
		// We use this for POS matching in `select`
		needsContext: new RegExp( "^" + whitespace +
			"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
			"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
	}, filterMatchExpr ),

	rinputs = /^(?:input|select|textarea|button)$/i,
	rheader = /^h\d$/i,

	// Easily-parseable/retrievable ID or TAG or CLASS selectors
	rquickExpr$1 = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

	// Used for iframes; see `setDocument`.
	// Support: IE 9 - 11+
	// Removing the function wrapper causes a "Permission Denied"
	// error in IE.
	unloadHandler = function() {
		setDocument();
	},

	inDisabledFieldset = addCombinator(
		function( elem ) {
			return elem.disabled === true && nodeName( elem, "fieldset" );
		},
		{ dir: "parentNode", next: "legend" }
	);

function find( selector, context, results, seed ) {
	var m, i, elem, nid, match, groups, newSelector,
		newContext = context && context.ownerDocument,

		// nodeType defaults to 9, since context defaults to document
		nodeType = context ? context.nodeType : 9;

	results = results || [];

	// Return early from calls with invalid selector or context
	if ( typeof selector !== "string" || !selector ||
		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {

		return results;
	}

	// Try to shortcut find operations (as opposed to filters) in HTML documents
	if ( !seed ) {
		setDocument( context );
		context = context || document;

		if ( documentIsHTML ) {

			// If the selector is sufficiently simple, try using a "get*By*" DOM method
			// (excepting DocumentFragment context, where the methods don't exist)
			if ( nodeType !== 11 && ( match = rquickExpr$1.exec( selector ) ) ) {

				// ID selector
				if ( ( m = match[ 1 ] ) ) {

					// Document context
					if ( nodeType === 9 ) {
						if ( ( elem = context.getElementById( m ) ) ) {
							push.call( results, elem );
						}
						return results;

					// Element context
					} else {
						if ( newContext && ( elem = newContext.getElementById( m ) ) &&
							jQuery.contains( context, elem ) ) {

							push.call( results, elem );
							return results;
						}
					}

				// Type selector
				} else if ( match[ 2 ] ) {
					push.apply( results, context.getElementsByTagName( selector ) );
					return results;

				// Class selector
				} else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) {
					push.apply( results, context.getElementsByClassName( m ) );
					return results;
				}
			}

			// Take advantage of querySelectorAll
			if ( !nonnativeSelectorCache[ selector + " " ] &&
				( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) {

				newSelector = selector;
				newContext = context;

				// qSA considers elements outside a scoping root when evaluating child or
				// descendant combinators, which is not what we want.
				// In such cases, we work around the behavior by prefixing every selector in the
				// list with an ID selector referencing the scope context.
				// The technique has to be used as well when a leading combinator is used
				// as such selectors are not recognized by querySelectorAll.
				// Thanks to Andrew Dupont for this technique.
				if ( nodeType === 1 &&
					( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {

					// Expand context for sibling selectors
					newContext = rsibling.test( selector ) &&
						testContext( context.parentNode ) ||
						context;

					// Outside of IE, if we're not changing the context we can
					// use :scope instead of an ID.
					// Support: IE 11+
					// IE sometimes throws a "Permission denied" error when strict-comparing
					// two documents; shallow comparisons work.
					// eslint-disable-next-line eqeqeq
					if ( newContext != context || isIE ) {

						// Capture the context ID, setting it first if necessary
						if ( ( nid = context.getAttribute( "id" ) ) ) {
							nid = jQuery.escapeSelector( nid );
						} else {
							context.setAttribute( "id", ( nid = jQuery.expando ) );
						}
					}

					// Prefix every selector in the list
					groups = tokenize( selector );
					i = groups.length;
					while ( i-- ) {
						groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
							toSelector( groups[ i ] );
					}
					newSelector = groups.join( "," );
				}

				try {
					push.apply( results,
						newContext.querySelectorAll( newSelector )
					);
					return results;
				} catch ( qsaError ) {
					nonnativeSelectorCache( selector, true );
				} finally {
					if ( nid === jQuery.expando ) {
						context.removeAttribute( "id" );
					}
				}
			}
		}
	}

	// All others
	return select( selector.replace( rtrimCSS, "$1" ), context, results, seed );
}

/**
 * Mark a function for special use by jQuery selector module
 * @param {Function} fn The function to mark
 */
function markFunction( fn ) {
	fn[ jQuery.expando ] = true;
	return fn;
}

/**
 * Returns a function to use in pseudos for input types
 * @param {String} type
 */
function createInputPseudo( type ) {
	return function( elem ) {
		return nodeName( elem, "input" ) && elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for buttons
 * @param {String} type
 */
function createButtonPseudo( type ) {
	return function( elem ) {
		return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) &&
			elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for :enabled/:disabled
 * @param {Boolean} disabled true for :disabled; false for :enabled
 */
function createDisabledPseudo( disabled ) {

	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
	return function( elem ) {

		// Only certain elements can match :enabled or :disabled
		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
		if ( "form" in elem ) {

			// Check for inherited disabledness on relevant non-disabled elements:
			// * listed form-associated elements in a disabled fieldset
			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
			// * option elements in a disabled optgroup
			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
			// All such elements have a "form" property.
			if ( elem.parentNode && elem.disabled === false ) {

				// Option elements defer to a parent optgroup if present
				if ( "label" in elem ) {
					if ( "label" in elem.parentNode ) {
						return elem.parentNode.disabled === disabled;
					} else {
						return elem.disabled === disabled;
					}
				}

				// Support: IE 6 - 11+
				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
				return elem.isDisabled === disabled ||

					// Where there is no isDisabled, check manually
					elem.isDisabled !== !disabled &&
						inDisabledFieldset( elem ) === disabled;
			}

			return elem.disabled === disabled;

		// Try to winnow out elements that can't be disabled before trusting the disabled property.
		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
		// even exist on them, let alone have a boolean value.
		} else if ( "label" in elem ) {
			return elem.disabled === disabled;
		}

		// Remaining elements are neither :enabled nor :disabled
		return false;
	};
}

/**
 * Returns a function to use in pseudos for positionals
 * @param {Function} fn
 */
function createPositionalPseudo( fn ) {
	return markFunction( function( argument ) {
		argument = +argument;
		return markFunction( function( seed, matches ) {
			var j,
				matchIndexes = fn( [], seed.length, argument ),
				i = matchIndexes.length;

			// Match elements found at the specified indexes
			while ( i-- ) {
				if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
					seed[ j ] = !( matches[ j ] = seed[ j ] );
				}
			}
		} );
	} );
}

/**
 * Sets document-related variables once based on the current document
 * @param {Element|Object} [node] An element or document object to use to set the document
 */
function setDocument( node ) {
	var subWindow,
		doc = node ? node.ownerDocument || node : document$1;

	// Return early if doc is invalid or already selected
	// Support: IE 11+
	// IE sometimes throws a "Permission denied" error when strict-comparing
	// two documents; shallow comparisons work.
	// eslint-disable-next-line eqeqeq
	if ( doc == document || doc.nodeType !== 9 ) {
		return;
	}

	// Update global variables
	document = doc;
	documentElement = document.documentElement;
	documentIsHTML = !jQuery.isXMLDoc( document );

	// Support: IE 9 - 11+
	// Accessing iframe documents after unload throws "permission denied" errors (see trac-13936)
	// Support: IE 11+
	// IE sometimes throws a "Permission denied" error when strict-comparing
	// two documents; shallow comparisons work.
	// eslint-disable-next-line eqeqeq
	if ( isIE && document$1 != document &&
		( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
		subWindow.addEventListener( "unload", unloadHandler );
	}
}

find.matches = function( expr, elements ) {
	return find( expr, null, null, elements );
};

find.matchesSelector = function( elem, expr ) {
	setDocument( elem );

	if ( documentIsHTML &&
		!nonnativeSelectorCache[ expr + " " ] &&
		( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {

		try {
			return matches.call( elem, expr );
		} catch ( e ) {
			nonnativeSelectorCache( expr, true );
		}
	}

	return find( expr, document, null, [ elem ] ).length > 0;
};

jQuery.expr = {

	// Can be adjusted by the user
	cacheLength: 50,

	createPseudo: markFunction,

	match: matchExpr,

	find: {
		ID: function( id, context ) {
			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
				var elem = context.getElementById( id );
				return elem ? [ elem ] : [];
			}
		},

		TAG: function( tag, context ) {
			if ( typeof context.getElementsByTagName !== "undefined" ) {
				return context.getElementsByTagName( tag );

				// DocumentFragment nodes don't have gEBTN
			} else {
				return context.querySelectorAll( tag );
			}
		},

		CLASS: function( className, context ) {
			if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
				return context.getElementsByClassName( className );
			}
		}
	},

	relative: {
		">": { dir: "parentNode", first: true },
		" ": { dir: "parentNode" },
		"+": { dir: "previousSibling", first: true },
		"~": { dir: "previousSibling" }
	},

	preFilter: preFilter,

	filter: {
		ID: function( id ) {
			var attrId = unescapeSelector( id );
			return function( elem ) {
				return elem.getAttribute( "id" ) === attrId;
			};
		},

		TAG: function( nodeNameSelector ) {
			var expectedNodeName = unescapeSelector( nodeNameSelector ).toLowerCase();
			return nodeNameSelector === "*" ?

				function() {
					return true;
				} :

				function( elem ) {
					return nodeName( elem, expectedNodeName );
				};
		},

		CLASS: function( className ) {
			var pattern = classCache[ className + " " ];

			return pattern ||
				( pattern = new RegExp( "(^|" + whitespace + ")" + className +
					"(" + whitespace + "|$)" ) ) &&
				classCache( className, function( elem ) {
					return pattern.test(
						typeof elem.className === "string" && elem.className ||
							typeof elem.getAttribute !== "undefined" &&
								elem.getAttribute( "class" ) ||
							""
					);
				} );
		},

		ATTR: function( name, operator, check ) {
			return function( elem ) {
				var result = jQuery.attr( elem, name );

				if ( result == null ) {
					return operator === "!=";
				}
				if ( !operator ) {
					return true;
				}

				result += "";

				if ( operator === "=" ) {
					return result === check;
				}
				if ( operator === "!=" ) {
					return result !== check;
				}
				if ( operator === "^=" ) {
					return check && result.indexOf( check ) === 0;
				}
				if ( operator === "*=" ) {
					return check && result.indexOf( check ) > -1;
				}
				if ( operator === "$=" ) {
					return check && result.slice( -check.length ) === check;
				}
				if ( operator === "~=" ) {
					return ( " " + result.replace( rwhitespace, " " ) + " " )
						.indexOf( check ) > -1;
				}
				if ( operator === "|=" ) {
					return result === check || result.slice( 0, check.length + 1 ) === check + "-";
				}

				return false;
			};
		},

		CHILD: function( type, what, _argument, first, last ) {
			var simple = type.slice( 0, 3 ) !== "nth",
				forward = type.slice( -4 ) !== "last",
				ofType = what === "of-type";

			return first === 1 && last === 0 ?

				// Shortcut for :nth-*(n)
				function( elem ) {
					return !!elem.parentNode;
				} :

				function( elem, _context, xml ) {
					var cache, outerCache, node, nodeIndex, start,
						dir = simple !== forward ? "nextSibling" : "previousSibling",
						parent = elem.parentNode,
						name = ofType && elem.nodeName.toLowerCase(),
						useCache = !xml && !ofType,
						diff = false;

					if ( parent ) {

						// :(first|last|only)-(child|of-type)
						if ( simple ) {
							while ( dir ) {
								node = elem;
								while ( ( node = node[ dir ] ) ) {
									if ( ofType ?
										nodeName( node, name ) :
										node.nodeType === 1 ) {

										return false;
									}
								}

								// Reverse direction for :only-* (if we haven't yet done so)
								start = dir = type === "only" && !start && "nextSibling";
							}
							return true;
						}

						start = [ forward ? parent.firstChild : parent.lastChild ];

						// non-xml :nth-child(...) stores cache data on `parent`
						if ( forward && useCache ) {

							// Seek `elem` from a previously-cached index
							outerCache = parent[ jQuery.expando ] ||
								( parent[ jQuery.expando ] = {} );
							cache = outerCache[ type ] || [];
							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
							diff = nodeIndex && cache[ 2 ];
							node = nodeIndex && parent.childNodes[ nodeIndex ];

							while ( ( node = ++nodeIndex && node && node[ dir ] ||

								// Fallback to seeking `elem` from the start
								( diff = nodeIndex = 0 ) || start.pop() ) ) {

								// When found, cache indexes on `parent` and break
								if ( node.nodeType === 1 && ++diff && node === elem ) {
									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
									break;
								}
							}

						} else {

							// Use previously-cached element index if available
							if ( useCache ) {
								outerCache = elem[ jQuery.expando ] ||
									( elem[ jQuery.expando ] = {} );
								cache = outerCache[ type ] || [];
								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
								diff = nodeIndex;
							}

							// xml :nth-child(...)
							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
							if ( diff === false ) {

								// Use the same loop as above to seek `elem` from the start
								while ( ( node = ++nodeIndex && node && node[ dir ] ||
									( diff = nodeIndex = 0 ) || start.pop() ) ) {

									if ( ( ofType ?
										nodeName( node, name ) :
										node.nodeType === 1 ) &&
										++diff ) {

										// Cache the index of each encountered element
										if ( useCache ) {
											outerCache = node[ jQuery.expando ] ||
												( node[ jQuery.expando ] = {} );
											outerCache[ type ] = [ dirruns, diff ];
										}

										if ( node === elem ) {
											break;
										}
									}
								}
							}
						}

						// Incorporate the offset, then check against cycle size
						diff -= last;
						return diff === first || ( diff % first === 0 && diff / first >= 0 );
					}
				};
		},

		PSEUDO: function( pseudo, argument ) {

			// pseudo-class names are case-insensitive
			// https://www.w3.org/TR/selectors/#pseudo-classes
			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
			// Remember that setFilters inherits from pseudos
			var fn = jQuery.expr.pseudos[ pseudo ] ||
				jQuery.expr.setFilters[ pseudo.toLowerCase() ] ||
				selectorError( "unsupported pseudo: " + pseudo );

			// The user may use createPseudo to indicate that
			// arguments are needed to create the filter function
			// just as jQuery does
			if ( fn[ jQuery.expando ] ) {
				return fn( argument );
			}

			return fn;
		}
	},

	pseudos: {

		// Potentially complex pseudos
		not: markFunction( function( selector ) {

			// Trim the selector passed to compile
			// to avoid treating leading and trailing
			// spaces as combinators
			var input = [],
				results = [],
				matcher = compile( selector.replace( rtrimCSS, "$1" ) );

			return matcher[ jQuery.expando ] ?
				markFunction( function( seed, matches, _context, xml ) {
					var elem,
						unmatched = matcher( seed, null, xml, [] ),
						i = seed.length;

					// Match elements unmatched by `matcher`
					while ( i-- ) {
						if ( ( elem = unmatched[ i ] ) ) {
							seed[ i ] = !( matches[ i ] = elem );
						}
					}
				} ) :
				function( elem, _context, xml ) {
					input[ 0 ] = elem;
					matcher( input, null, xml, results );

					// Don't keep the element
					// (see https://github.com/jquery/sizzle/issues/299)
					input[ 0 ] = null;
					return !results.pop();
				};
		} ),

		has: markFunction( function( selector ) {
			return function( elem ) {
				return find( selector, elem ).length > 0;
			};
		} ),

		contains: markFunction( function( text ) {
			text = unescapeSelector( text );
			return function( elem ) {
				return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
			};
		} ),

		// "Whether an element is represented by a :lang() selector
		// is based solely on the element's language value
		// being equal to the identifier C,
		// or beginning with the identifier C immediately followed by "-".
		// The matching of C against the element's language value is performed case-insensitively.
		// The identifier C does not have to be a valid language name."
		// https://www.w3.org/TR/selectors/#lang-pseudo
		lang: markFunction( function( lang ) {

			// lang value must be a valid identifier
			if ( !ridentifier.test( lang || "" ) ) {
				selectorError( "unsupported lang: " + lang );
			}
			lang = unescapeSelector( lang ).toLowerCase();
			return function( elem ) {
				var elemLang;
				do {
					if ( ( elemLang = documentIsHTML ?
						elem.lang :
						elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {

						elemLang = elemLang.toLowerCase();
						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
					}
				} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
				return false;
			};
		} ),

		// Miscellaneous
		target: function( elem ) {
			var hash = window.location && window.location.hash;
			return hash && hash.slice( 1 ) === elem.id;
		},

		root: function( elem ) {
			return elem === documentElement;
		},

		focus: function( elem ) {
			return elem === document.activeElement &&
				document.hasFocus() &&
				!!( elem.type || elem.href || ~elem.tabIndex );
		},

		// Boolean properties
		enabled: createDisabledPseudo( false ),
		disabled: createDisabledPseudo( true ),

		checked: function( elem ) {

			// In CSS3, :checked should return both checked and selected elements
			// https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
			return ( nodeName( elem, "input" ) && !!elem.checked ) ||
				( nodeName( elem, "option" ) && !!elem.selected );
		},

		selected: function( elem ) {

			// Support: IE <=11+
			// Accessing the selectedIndex property
			// forces the browser to treat the default option as
			// selected when in an optgroup.
			if ( isIE && elem.parentNode ) {
				// eslint-disable-next-line no-unused-expressions
				elem.parentNode.selectedIndex;
			}

			return elem.selected === true;
		},

		// Contents
		empty: function( elem ) {

			// https://www.w3.org/TR/selectors/#empty-pseudo
			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
			//   but not by others (comment: 8; processing instruction: 7; etc.)
			// nodeType < 6 works because attributes (2) do not appear as children
			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
				if ( elem.nodeType < 6 ) {
					return false;
				}
			}
			return true;
		},

		parent: function( elem ) {
			return !jQuery.expr.pseudos.empty( elem );
		},

		// Element/input types
		header: function( elem ) {
			return rheader.test( elem.nodeName );
		},

		input: function( elem ) {
			return rinputs.test( elem.nodeName );
		},

		button: function( elem ) {
			return nodeName( elem, "input" ) && elem.type === "button" ||
				nodeName( elem, "button" );
		},

		text: function( elem ) {
			return nodeName( elem, "input" ) && elem.type === "text";
		},

		// Position-in-collection
		first: createPositionalPseudo( function() {
			return [ 0 ];
		} ),

		last: createPositionalPseudo( function( _matchIndexes, length ) {
			return [ length - 1 ];
		} ),

		eq: createPositionalPseudo( function( _matchIndexes, length, argument ) {
			return [ argument < 0 ? argument + length : argument ];
		} ),

		even: createPositionalPseudo( function( matchIndexes, length ) {
			var i = 0;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		} ),

		odd: createPositionalPseudo( function( matchIndexes, length ) {
			var i = 1;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		} ),

		lt: createPositionalPseudo( function( matchIndexes, length, argument ) {
			var i;

			if ( argument < 0 ) {
				i = argument + length;
			} else if ( argument > length ) {
				i = length;
			} else {
				i = argument;
			}

			for ( ; --i >= 0; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		} ),

		gt: createPositionalPseudo( function( matchIndexes, length, argument ) {
			var i = argument < 0 ? argument + length : argument;
			for ( ; ++i < length; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		} )
	}
};

jQuery.expr.pseudos.nth = jQuery.expr.pseudos.eq;

// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
	jQuery.expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
	jQuery.expr.pseudos[ i ] = createButtonPseudo( i );
}

// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = jQuery.expr.pseudos;
jQuery.expr.setFilters = new setFilters();

function addCombinator( matcher, combinator, base ) {
	var dir = combinator.dir,
		skip = combinator.next,
		key = skip || dir,
		checkNonElements = base && key === "parentNode",
		doneName = done++;

	return combinator.first ?

		// Check against closest ancestor/preceding element
		function( elem, context, xml ) {
			while ( ( elem = elem[ dir ] ) ) {
				if ( elem.nodeType === 1 || checkNonElements ) {
					return matcher( elem, context, xml );
				}
			}
			return false;
		} :

		// Check against all ancestor/preceding elements
		function( elem, context, xml ) {
			var oldCache, outerCache,
				newCache = [ dirruns, doneName ];

			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
			if ( xml ) {
				while ( ( elem = elem[ dir ] ) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						if ( matcher( elem, context, xml ) ) {
							return true;
						}
					}
				}
			} else {
				while ( ( elem = elem[ dir ] ) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						outerCache = elem[ jQuery.expando ] || ( elem[ jQuery.expando ] = {} );

						if ( skip && nodeName( elem, skip ) ) {
							elem = elem[ dir ] || elem;
						} else if ( ( oldCache = outerCache[ key ] ) &&
							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {

							// Assign to newCache so results back-propagate to previous elements
							return ( newCache[ 2 ] = oldCache[ 2 ] );
						} else {

							// Reuse newcache so results back-propagate to previous elements
							outerCache[ key ] = newCache;

							// A match means we're done; a fail means we have to keep checking
							if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
								return true;
							}
						}
					}
				}
			}
			return false;
		};
}

function elementMatcher( matchers ) {
	return matchers.length > 1 ?
		function( elem, context, xml ) {
			var i = matchers.length;
			while ( i-- ) {
				if ( !matchers[ i ]( elem, context, xml ) ) {
					return false;
				}
			}
			return true;
		} :
		matchers[ 0 ];
}

function multipleContexts( selector, contexts, results ) {
	var i = 0,
		len = contexts.length;
	for ( ; i < len; i++ ) {
		find( selector, contexts[ i ], results );
	}
	return results;
}

function condense( unmatched, map, filter, context, xml ) {
	var elem,
		newUnmatched = [],
		i = 0,
		len = unmatched.length,
		mapped = map != null;

	for ( ; i < len; i++ ) {
		if ( ( elem = unmatched[ i ] ) ) {
			if ( !filter || filter( elem, context, xml ) ) {
				newUnmatched.push( elem );
				if ( mapped ) {
					map.push( i );
				}
			}
		}
	}

	return newUnmatched;
}

function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
	if ( postFilter && !postFilter[ jQuery.expando ] ) {
		postFilter = setMatcher( postFilter );
	}
	if ( postFinder && !postFinder[ jQuery.expando ] ) {
		postFinder = setMatcher( postFinder, postSelector );
	}
	return markFunction( function( seed, results, context, xml ) {
		var temp, i, elem, matcherOut,
			preMap = [],
			postMap = [],
			preexisting = results.length,

			// Get initial elements from seed or context
			elems = seed ||
				multipleContexts( selector || "*",
					context.nodeType ? [ context ] : context, [] ),

			// Prefilter to get matcher input, preserving a map for seed-results synchronization
			matcherIn = preFilter && ( seed || !selector ) ?
				condense( elems, preMap, preFilter, context, xml ) :
				elems;

		if ( matcher ) {

			// If we have a postFinder, or filtered seed, or non-seed postFilter
			// or preexisting results,
			matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ?

				// ...intermediate processing is necessary
				[] :

				// ...otherwise use results directly
				results;

			// Find primary matches
			matcher( matcherIn, matcherOut, context, xml );
		} else {
			matcherOut = matcherIn;
		}

		// Apply postFilter
		if ( postFilter ) {
			temp = condense( matcherOut, postMap );
			postFilter( temp, [], context, xml );

			// Un-match failing elements by moving them back to matcherIn
			i = temp.length;
			while ( i-- ) {
				if ( ( elem = temp[ i ] ) ) {
					matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
				}
			}
		}

		if ( seed ) {
			if ( postFinder || preFilter ) {
				if ( postFinder ) {

					// Get the final matcherOut by condensing this intermediate into postFinder contexts
					temp = [];
					i = matcherOut.length;
					while ( i-- ) {
						if ( ( elem = matcherOut[ i ] ) ) {

							// Restore matcherIn since elem is not yet a final match
							temp.push( ( matcherIn[ i ] = elem ) );
						}
					}
					postFinder( null, ( matcherOut = [] ), temp, xml );
				}

				// Move matched elements from seed to results to keep them synchronized
				i = matcherOut.length;
				while ( i-- ) {
					if ( ( elem = matcherOut[ i ] ) &&
						( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) {

						seed[ temp ] = !( results[ temp ] = elem );
					}
				}
			}

		// Add elements to results, through postFinder if defined
		} else {
			matcherOut = condense(
				matcherOut === results ?
					matcherOut.splice( preexisting, matcherOut.length ) :
					matcherOut
			);
			if ( postFinder ) {
				postFinder( null, results, matcherOut, xml );
			} else {
				push.apply( results, matcherOut );
			}
		}
	} );
}

function matcherFromTokens( tokens ) {
	var checkContext, matcher, j,
		len = tokens.length,
		leadingRelative = jQuery.expr.relative[ tokens[ 0 ].type ],
		implicitRelative = leadingRelative || jQuery.expr.relative[ " " ],
		i = leadingRelative ? 1 : 0,

		// The foundational matcher ensures that elements are reachable from top-level context(s)
		matchContext = addCombinator( function( elem ) {
			return elem === checkContext;
		}, implicitRelative, true ),
		matchAnyContext = addCombinator( function( elem ) {
			return indexOf.call( checkContext, elem ) > -1;
		}, implicitRelative, true ),
		matchers = [ function( elem, context, xml ) {

			// Support: IE 11+
			// IE sometimes throws a "Permission denied" error when strict-comparing
			// two documents; shallow comparisons work.
			// eslint-disable-next-line eqeqeq
			var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || (
				( checkContext = context ).nodeType ?
					matchContext( elem, context, xml ) :
					matchAnyContext( elem, context, xml ) );

			// Avoid hanging onto element
			// (see https://github.com/jquery/sizzle/issues/299)
			checkContext = null;
			return ret;
		} ];

	for ( ; i < len; i++ ) {
		if ( ( matcher = jQuery.expr.relative[ tokens[ i ].type ] ) ) {
			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
		} else {
			matcher = jQuery.expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );

			// Return special upon seeing a positional matcher
			if ( matcher[ jQuery.expando ] ) {

				// Find the next relative operator (if any) for proper handling
				j = ++i;
				for ( ; j < len; j++ ) {
					if ( jQuery.expr.relative[ tokens[ j ].type ] ) {
						break;
					}
				}
				return setMatcher(
					i > 1 && elementMatcher( matchers ),
					i > 1 && toSelector(

						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
						tokens.slice( 0, i - 1 )
							.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
					).replace( rtrimCSS, "$1" ),
					matcher,
					i < j && matcherFromTokens( tokens.slice( i, j ) ),
					j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
					j < len && toSelector( tokens )
				);
			}
			matchers.push( matcher );
		}
	}

	return elementMatcher( matchers );
}

function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
	var bySet = setMatchers.length > 0,
		byElement = elementMatchers.length > 0,
		superMatcher = function( seed, context, xml, results, outermost ) {
			var elem, j, matcher,
				matchedCount = 0,
				i = "0",
				unmatched = seed && [],
				setMatched = [],
				contextBackup = outermostContext,

				// We must always have either seed elements or outermost context
				elems = seed || byElement && jQuery.expr.find.TAG( "*", outermost ),

				// Use integer dirruns iff this is the outermost matcher
				dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 );

			if ( outermost ) {

				// Support: IE 11+
				// IE sometimes throws a "Permission denied" error when strict-comparing
				// two documents; shallow comparisons work.
				// eslint-disable-next-line eqeqeq
				outermostContext = context == document || context || outermost;
			}

			// Add elements passing elementMatchers directly to results
			for ( ; ( elem = elems[ i ] ) != null; i++ ) {
				if ( byElement && elem ) {
					j = 0;

					// Support: IE 11+
					// IE sometimes throws a "Permission denied" error when strict-comparing
					// two documents; shallow comparisons work.
					// eslint-disable-next-line eqeqeq
					if ( !context && elem.ownerDocument != document ) {
						setDocument( elem );
						xml = !documentIsHTML;
					}
					while ( ( matcher = elementMatchers[ j++ ] ) ) {
						if ( matcher( elem, context || document, xml ) ) {
							push.call( results, elem );
							break;
						}
					}
					if ( outermost ) {
						dirruns = dirrunsUnique;
					}
				}

				// Track unmatched elements for set filters
				if ( bySet ) {

					// They will have gone through all possible matchers
					if ( ( elem = !matcher && elem ) ) {
						matchedCount--;
					}

					// Lengthen the array for every element, matched or not
					if ( seed ) {
						unmatched.push( elem );
					}
				}
			}

			// `i` is now the count of elements visited above, and adding it to `matchedCount`
			// makes the latter nonnegative.
			matchedCount += i;

			// Apply set filters to unmatched elements
			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
			// no element matchers and no seed.
			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
			// numerically zero.
			if ( bySet && i !== matchedCount ) {
				j = 0;
				while ( ( matcher = setMatchers[ j++ ] ) ) {
					matcher( unmatched, setMatched, context, xml );
				}

				if ( seed ) {

					// Reintegrate element matches to eliminate the need for sorting
					if ( matchedCount > 0 ) {
						while ( i-- ) {
							if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
								setMatched[ i ] = pop.call( results );
							}
						}
					}

					// Discard index placeholder values to get only actual matches
					setMatched = condense( setMatched );
				}

				// Add matches to results
				push.apply( results, setMatched );

				// Seedless set matches succeeding multiple successful matchers stipulate sorting
				if ( outermost && !seed && setMatched.length > 0 &&
					( matchedCount + setMatchers.length ) > 1 ) {

					jQuery.uniqueSort( results );
				}
			}

			// Override manipulation of globals by nested matchers
			if ( outermost ) {
				dirruns = dirrunsUnique;
				outermostContext = contextBackup;
			}

			return unmatched;
		};

	return bySet ?
		markFunction( superMatcher ) :
		superMatcher;
}

function compile( selector, match /* Internal Use Only */ ) {
	var i,
		setMatchers = [],
		elementMatchers = [],
		cached = compilerCache[ selector + " " ];

	if ( !cached ) {

		// Generate a function of recursive functions that can be used to check each element
		if ( !match ) {
			match = tokenize( selector );
		}
		i = match.length;
		while ( i-- ) {
			cached = matcherFromTokens( match[ i ] );
			if ( cached[ jQuery.expando ] ) {
				setMatchers.push( cached );
			} else {
				elementMatchers.push( cached );
			}
		}

		// Cache the compiled function
		cached = compilerCache( selector,
			matcherFromGroupMatchers( elementMatchers, setMatchers ) );

		// Save selector and tokenization
		cached.selector = selector;
	}
	return cached;
}

/**
 * A low-level selection function that works with jQuery's compiled
 *  selector functions
 * @param {String|Function} selector A selector or a pre-compiled
 *  selector function built with jQuery selector compile
 * @param {Element} context
 * @param {Array} [results]
 * @param {Array} [seed] A set of elements to match against
 */
function select( selector, context, results, seed ) {
	var i, tokens, token, type, find,
		compiled = typeof selector === "function" && selector,
		match = !seed && tokenize( ( selector = compiled.selector || selector ) );

	results = results || [];

	// Try to minimize operations if there is only one selector in the list and no seed
	// (the latter of which guarantees us context)
	if ( match.length === 1 ) {

		// Reduce context if the leading compound selector is an ID
		tokens = match[ 0 ] = match[ 0 ].slice( 0 );
		if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
				context.nodeType === 9 && documentIsHTML &&
				jQuery.expr.relative[ tokens[ 1 ].type ] ) {

			context = ( jQuery.expr.find.ID(
				unescapeSelector( token.matches[ 0 ] ),
				context
			) || [] )[ 0 ];
			if ( !context ) {
				return results;

			// Precompiled matchers will still verify ancestry, so step up a level
			} else if ( compiled ) {
				context = context.parentNode;
			}

			selector = selector.slice( tokens.shift().value.length );
		}

		// Fetch a seed set for right-to-left matching
		i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length;
		while ( i-- ) {
			token = tokens[ i ];

			// Abort if we hit a combinator
			if ( jQuery.expr.relative[ ( type = token.type ) ] ) {
				break;
			}
			if ( ( find = jQuery.expr.find[ type ] ) ) {

				// Search, expanding context for leading sibling combinators
				if ( ( seed = find(
					unescapeSelector( token.matches[ 0 ] ),
					rsibling.test( tokens[ 0 ].type ) &&
						testContext( context.parentNode ) || context
				) ) ) {

					// If seed is empty or no tokens remain, we can return early
					tokens.splice( i, 1 );
					selector = seed.length && toSelector( tokens );
					if ( !selector ) {
						push.apply( results, seed );
						return results;
					}

					break;
				}
			}
		}
	}

	// Compile and execute a filtering function if one is not provided
	// Provide `match` to avoid retokenization if we modified the selector above
	( compiled || compile( selector, match ) )(
		seed,
		context,
		!documentIsHTML,
		results,
		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
	);
	return results;
}

// Initialize against the default document
setDocument();

jQuery.find = find;

// These have always been private, but they used to be documented as part of
// Sizzle so let's maintain them for now for backwards compatibility purposes.
find.compile = compile;
find.select = select;
find.setDocument = setDocument;
find.tokenize = tokenize;

function dir( elem, dir, until ) {
	var matched = [],
		truncate = until !== undefined;

	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
		if ( elem.nodeType === 1 ) {
			if ( truncate && jQuery( elem ).is( until ) ) {
				break;
			}
			matched.push( elem );
		}
	}
	return matched;
}

function siblings( n, elem ) {
	var matched = [];

	for ( ; n; n = n.nextSibling ) {
		if ( n.nodeType === 1 && n !== elem ) {
			matched.push( n );
		}
	}

	return matched;
}

var rneedsContext = jQuery.expr.match.needsContext;

// rsingleTag matches a string consisting of a single HTML element with no attributes
// and captures the element's name
var rsingleTag = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;

function isObviousHtml( input ) {
	return input[ 0 ] === "<" &&
		input[ input.length - 1 ] === ">" &&
		input.length >= 3;
}

// Implement the identical functionality for filter and not
function winnow( elements, qualifier, not ) {
	if ( typeof qualifier === "function" ) {
		return jQuery.grep( elements, function( elem, i ) {
			return !!qualifier.call( elem, i, elem ) !== not;
		} );
	}

	// Single element
	if ( qualifier.nodeType ) {
		return jQuery.grep( elements, function( elem ) {
			return ( elem === qualifier ) !== not;
		} );
	}

	// Arraylike of elements (jQuery, arguments, Array)
	if ( typeof qualifier !== "string" ) {
		return jQuery.grep( elements, function( elem ) {
			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
		} );
	}

	// Filtered directly for both simple and complex selectors
	return jQuery.filter( qualifier, elements, not );
}

jQuery.filter = function( expr, elems, not ) {
	var elem = elems[ 0 ];

	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	if ( elems.length === 1 && elem.nodeType === 1 ) {
		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
	}

	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
		return elem.nodeType === 1;
	} ) );
};

jQuery.fn.extend( {
	find: function( selector ) {
		var i, ret,
			len = this.length,
			self = this;

		if ( typeof selector !== "string" ) {
			return this.pushStack( jQuery( selector ).filter( function() {
				for ( i = 0; i < len; i++ ) {
					if ( jQuery.contains( self[ i ], this ) ) {
						return true;
					}
				}
			} ) );
		}

		ret = this.pushStack( [] );

		for ( i = 0; i < len; i++ ) {
			jQuery.find( selector, self[ i ], ret );
		}

		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
	},
	filter: function( selector ) {
		return this.pushStack( winnow( this, selector || [], false ) );
	},
	not: function( selector ) {
		return this.pushStack( winnow( this, selector || [], true ) );
	},
	is: function( selector ) {
		return !!winnow(
			this,

			// If this is a positional/relative selector, check membership in the returned set
			// so $("p:first").is("p:last") won't return true for a doc with two "p".
			typeof selector === "string" && rneedsContext.test( selector ) ?
				jQuery( selector ) :
				selector || [],
			false
		).length;
	}
} );

// Initialize a jQuery object

// A central reference to the root jQuery(document)
var rootjQuery,

	// A simple way to check for HTML strings
	// Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521)
	// Strict HTML recognition (trac-11290: must start with <)
	// Shortcut simple #id case for speed
	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,

	init = jQuery.fn.init = function( selector, context ) {
		var match, elem;

		// HANDLE: $(""), $(null), $(undefined), $(false)
		if ( !selector ) {
			return this;
		}

		// HANDLE: $(DOMElement)
		if ( selector.nodeType ) {
			this[ 0 ] = selector;
			this.length = 1;
			return this;

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( typeof selector === "function" ) {
			return rootjQuery.ready !== undefined ?
				rootjQuery.ready( selector ) :

				// Execute immediately if ready is not present
				selector( jQuery );

		} else {

			// Handle obvious HTML strings
			match = selector + "";
			if ( isObviousHtml( match ) ) {

				// Assume that strings that start and end with <> are HTML and skip
				// the regex check. This also handles browser-supported HTML wrappers
				// like TrustedHTML.
				match = [ null, selector, null ];

			// Handle HTML strings or selectors
			} else if ( typeof selector === "string" ) {
				match = rquickExpr.exec( selector );
			} else {
				return jQuery.makeArray( selector, this );
			}

			// Match html or make sure no context is specified for #id
			// Note: match[1] may be a string or a TrustedHTML wrapper
			if ( match && ( match[ 1 ] || !context ) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[ 1 ] ) {
					context = context instanceof jQuery ? context[ 0 ] : context;

					// Option to run scripts is true for back-compat
					// Intentionally let the error be thrown if parseHTML is not present
					jQuery.merge( this, jQuery.parseHTML(
						match[ 1 ],
						context && context.nodeType ? context.ownerDocument || context : document$1,
						true
					) );

					// HANDLE: $(html, props)
					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
						for ( match in context ) {

							// Properties of context are called as methods if possible
							if ( typeof this[ match ] === "function" ) {
								this[ match ]( context[ match ] );

							// ...and otherwise set as attributes
							} else {
								this.attr( match, context[ match ] );
							}
						}
					}

					return this;

				// HANDLE: $(#id)
				} else {
					elem = document$1.getElementById( match[ 2 ] );

					if ( elem ) {

						// Inject the element directly into the jQuery object
						this[ 0 ] = elem;
						this.length = 1;
					}
					return this;
				}

			// HANDLE: $(expr) & $(expr, $(...))
			} else if ( !context || context.jquery ) {
				return ( context || rootjQuery ).find( selector );

			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return this.constructor( context ).find( selector );
			}
		}

	};

// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

// Initialize central reference
rootjQuery = jQuery( document$1 );

var rparentsprev = /^(?:parents|prev(?:Until|All))/,

	// Methods guaranteed to produce a unique set when starting from a unique set
	guaranteedUnique = {
		children: true,
		contents: true,
		next: true,
		prev: true
	};

jQuery.fn.extend( {
	has: function( target ) {
		var targets = jQuery( target, this ),
			l = targets.length;

		return this.filter( function() {
			var i = 0;
			for ( ; i < l; i++ ) {
				if ( jQuery.contains( this, targets[ i ] ) ) {
					return true;
				}
			}
		} );
	},

	closest: function( selectors, context ) {
		var cur,
			i = 0,
			l = this.length,
			matched = [],
			targets = typeof selectors !== "string" && jQuery( selectors );

		// Positional selectors never match, since there's no _selection_ context
		if ( !rneedsContext.test( selectors ) ) {
			for ( ; i < l; i++ ) {
				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {

					// Always skip document fragments
					if ( cur.nodeType < 11 && ( targets ?
						targets.index( cur ) > -1 :

						// Don't pass non-elements to jQuery#find
						cur.nodeType === 1 &&
							jQuery.find.matchesSelector( cur, selectors ) ) ) {

						matched.push( cur );
						break;
					}
				}
			}
		}

		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
	},

	// Determine the position of an element within the set
	index: function( elem ) {

		// No argument, return index in parent
		if ( !elem ) {
			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
		}

		// Index in selector
		if ( typeof elem === "string" ) {
			return indexOf.call( jQuery( elem ), this[ 0 ] );
		}

		// Locate the position of the desired element
		return indexOf.call( this,

			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[ 0 ] : elem
		);
	},

	add: function( selector, context ) {
		return this.pushStack(
			jQuery.uniqueSort(
				jQuery.merge( this.get(), jQuery( selector, context ) )
			)
		);
	},

	addBack: function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	}
} );

function sibling( cur, dir ) {
	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
	return cur;
}

jQuery.each( {
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, _i, until ) {
		return dir( elem, "parentNode", until );
	},
	next: function( elem ) {
		return sibling( elem, "nextSibling" );
	},
	prev: function( elem ) {
		return sibling( elem, "previousSibling" );
	},
	nextAll: function( elem ) {
		return dir( elem, "nextSibling" );
	},
	prevAll: function( elem ) {
		return dir( elem, "previousSibling" );
	},
	nextUntil: function( elem, _i, until ) {
		return dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, _i, until ) {
		return dir( elem, "previousSibling", until );
	},
	siblings: function( elem ) {
		return siblings( ( elem.parentNode || {} ).firstChild, elem );
	},
	children: function( elem ) {
		return siblings( elem.firstChild );
	},
	contents: function( elem ) {
		if ( elem.contentDocument != null &&

			// Support: IE 11+
			// <object> elements with no `data` attribute has an object
			// `contentDocument` with a `null` prototype.
			getProto( elem.contentDocument ) ) {

			return elem.contentDocument;
		}

		// Support: IE 9 - 11+
		// Treat the template element as a regular one in browsers that
		// don't support it.
		if ( nodeName( elem, "template" ) ) {
			elem = elem.content || elem;
		}

		return jQuery.merge( [], elem.childNodes );
	}
}, function( name, fn ) {
	jQuery.fn[ name ] = function( until, selector ) {
		var matched = jQuery.map( this, fn, until );

		if ( name.slice( -5 ) !== "Until" ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			matched = jQuery.filter( selector, matched );
		}

		if ( this.length > 1 ) {

			// Remove duplicates
			if ( !guaranteedUnique[ name ] ) {
				jQuery.uniqueSort( matched );
			}

			// Reverse order for parents* and prev-derivatives
			if ( rparentsprev.test( name ) ) {
				matched.reverse();
			}
		}

		return this.pushStack( matched );
	};
} );

// Matches dashed string for camelizing
var rdashAlpha = /-([a-z])/g;

// Used by camelCase as callback to replace()
function fcamelCase( _all, letter ) {
	return letter.toUpperCase();
}

// Convert dashed to camelCase
function camelCase( string ) {
	return string.replace( rdashAlpha, fcamelCase );
}

/**
 * Determines whether an object can have data
 */
function acceptData( owner ) {

	// Accepts only:
	//  - Node
	//    - Node.ELEMENT_NODE
	//    - Node.DOCUMENT_NODE
	//  - Object
	//    - Any
	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
}

function Data() {
	this.expando = jQuery.expando + Data.uid++;
}

Data.uid = 1;

Data.prototype = {

	cache: function( owner ) {

		// Check if the owner object already has a cache
		var value = owner[ this.expando ];

		// If not, create one
		if ( !value ) {
			value = Object.create( null );

			// We can accept data for non-element nodes in modern browsers,
			// but we should not, see trac-8335.
			// Always return an empty object.
			if ( acceptData( owner ) ) {

				// If it is a node unlikely to be stringify-ed or looped over
				// use plain assignment
				if ( owner.nodeType ) {
					owner[ this.expando ] = value;

				// Otherwise secure it in a non-enumerable property
				// configurable must be true to allow the property to be
				// deleted when data is removed
				} else {
					Object.defineProperty( owner, this.expando, {
						value: value,
						configurable: true
					} );
				}
			}
		}

		return value;
	},
	set: function( owner, data, value ) {
		var prop,
			cache = this.cache( owner );

		// Handle: [ owner, key, value ] args
		// Always use camelCase key (gh-2257)
		if ( typeof data === "string" ) {
			cache[ camelCase( data ) ] = value;

		// Handle: [ owner, { properties } ] args
		} else {

			// Copy the properties one-by-one to the cache object
			for ( prop in data ) {
				cache[ camelCase( prop ) ] = data[ prop ];
			}
		}
		return value;
	},
	get: function( owner, key ) {
		return key === undefined ?
			this.cache( owner ) :

			// Always use camelCase key (gh-2257)
			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
	},
	access: function( owner, key, value ) {

		// In cases where either:
		//
		//   1. No key was specified
		//   2. A string key was specified, but no value provided
		//
		// Take the "read" path and allow the get method to determine
		// which value to return, respectively either:
		//
		//   1. The entire cache object
		//   2. The data stored at the key
		//
		if ( key === undefined ||
				( ( key && typeof key === "string" ) && value === undefined ) ) {

			return this.get( owner, key );
		}

		// When the key is not a string, or both a key and value
		// are specified, set or extend (existing objects) with either:
		//
		//   1. An object of properties
		//   2. A key and value
		//
		this.set( owner, key, value );

		// Since the "set" path can have two possible entry points
		// return the expected data based on which path was taken[*]
		return value !== undefined ? value : key;
	},
	remove: function( owner, key ) {
		var i,
			cache = owner[ this.expando ];

		if ( cache === undefined ) {
			return;
		}

		if ( key !== undefined ) {

			// Support array or space separated string of keys
			if ( Array.isArray( key ) ) {

				// If key is an array of keys...
				// We always set camelCase keys, so remove that.
				key = key.map( camelCase );
			} else {
				key = camelCase( key );

				// If a key with the spaces exists, use it.
				// Otherwise, create an array by matching non-whitespace
				key = key in cache ?
					[ key ] :
					( key.match( rnothtmlwhite ) || [] );
			}

			i = key.length;

			while ( i-- ) {
				delete cache[ key[ i ] ];
			}
		}

		// Remove the expando if there's no more data
		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

			// Support: Chrome <=35 - 45+
			// Webkit & Blink performance suffers when deleting properties
			// from DOM nodes, so set to undefined instead
			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
			if ( owner.nodeType ) {
				owner[ this.expando ] = undefined;
			} else {
				delete owner[ this.expando ];
			}
		}
	},
	hasData: function( owner ) {
		var cache = owner[ this.expando ];
		return cache !== undefined && !jQuery.isEmptyObject( cache );
	}
};

var dataPriv = new Data();

var dataUser = new Data();

//	Implementation Summary
//
//	1. Enforce API surface and semantic compatibility with 1.9.x branch
//	2. Improve the module's maintainability by reducing the storage
//		paths to a single mechanism.
//	3. Use the same single mechanism to support "private" and "user" data.
//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
//	5. Avoid exposing implementation details on user objects (eg. expando properties)
//	6. Provide a clear path for implementation upgrade to WeakMap in 2014

var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
	rmultiDash = /[A-Z]/g;

function getData( data ) {
	if ( data === "true" ) {
		return true;
	}

	if ( data === "false" ) {
		return false;
	}

	if ( data === "null" ) {
		return null;
	}

	// Only convert to a number if it doesn't change the string
	if ( data === +data + "" ) {
		return +data;
	}

	if ( rbrace.test( data ) ) {
		return JSON.parse( data );
	}

	return data;
}

function dataAttr( elem, key, data ) {
	var name;

	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {
		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
		data = elem.getAttribute( name );

		if ( typeof data === "string" ) {
			try {
				data = getData( data );
			} catch ( e ) {}

			// Make sure we set the data so it isn't changed later
			dataUser.set( elem, key, data );
		} else {
			data = undefined;
		}
	}
	return data;
}

jQuery.extend( {
	hasData: function( elem ) {
		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
	},

	data: function( elem, name, data ) {
		return dataUser.access( elem, name, data );
	},

	removeData: function( elem, name ) {
		dataUser.remove( elem, name );
	},

	// TODO: Now that all calls to _data and _removeData have been replaced
	// with direct calls to dataPriv methods, these can be deprecated.
	_data: function( elem, name, data ) {
		return dataPriv.access( elem, name, data );
	},

	_removeData: function( elem, name ) {
		dataPriv.remove( elem, name );
	}
} );

jQuery.fn.extend( {
	data: function( key, value ) {
		var i, name, data,
			elem = this[ 0 ],
			attrs = elem && elem.attributes;

		// Gets all values
		if ( key === undefined ) {
			if ( this.length ) {
				data = dataUser.get( elem );

				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
					i = attrs.length;
					while ( i-- ) {

						// Support: IE 11+
						// The attrs elements can be null (trac-14894)
						if ( attrs[ i ] ) {
							name = attrs[ i ].name;
							if ( name.indexOf( "data-" ) === 0 ) {
								name = camelCase( name.slice( 5 ) );
								dataAttr( elem, name, data[ name ] );
							}
						}
					}
					dataPriv.set( elem, "hasDataAttrs", true );
				}
			}

			return data;
		}

		// Sets multiple values
		if ( typeof key === "object" ) {
			return this.each( function() {
				dataUser.set( this, key );
			} );
		}

		return access( this, function( value ) {
			var data;

			// The calling jQuery object (element matches) is not empty
			// (and therefore has an element appears at this[ 0 ]) and the
			// `value` parameter was not undefined. An empty jQuery object
			// will result in `undefined` for elem = this[ 0 ] which will
			// throw an exception if an attempt to read a data cache is made.
			if ( elem && value === undefined ) {

				// Attempt to get data from the cache
				// The key will always be camelCased in Data
				data = dataUser.get( elem, key );
				if ( data !== undefined ) {
					return data;
				}

				// Attempt to "discover" the data in
				// HTML5 custom data-* attrs
				data = dataAttr( elem, key );
				if ( data !== undefined ) {
					return data;
				}

				// We tried really hard, but the data doesn't exist.
				return;
			}

			// Set the data...
			this.each( function() {

				// We always store the camelCased key
				dataUser.set( this, key, value );
			} );
		}, null, value, arguments.length > 1, null, true );
	},

	removeData: function( key ) {
		return this.each( function() {
			dataUser.remove( this, key );
		} );
	}
} );

var rfocusable = /^(?:input|select|textarea|button)$/i,
	rclickable = /^(?:a|area)$/i;

jQuery.fn.extend( {
	prop: function( name, value ) {
		return access( this, jQuery.prop, name, value, arguments.length > 1 );
	},

	removeProp: function( name ) {
		return this.each( function() {
			delete this[ jQuery.propFix[ name ] || name ];
		} );
	}
} );

jQuery.extend( {
	prop: function( elem, name, value ) {
		var ret, hooks,
			nType = elem.nodeType;

		// Don't get/set properties on text, comment and attribute nodes
		if ( nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {

			// Fix name and attach hooks
			name = jQuery.propFix[ name ] || name;
			hooks = jQuery.propHooks[ name ];
		}

		if ( value !== undefined ) {
			if ( hooks && "set" in hooks &&
				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
				return ret;
			}

			return ( elem[ name ] = value );
		}

		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
			return ret;
		}

		return elem[ name ];
	},

	propHooks: {
		tabIndex: {
			get: function( elem ) {

				// Support: IE <=9 - 11+
				// elem.tabIndex doesn't always return the
				// correct value when it hasn't been explicitly set
				// Use proper attribute retrieval (trac-12072)
				var tabindex = elem.getAttribute( "tabindex" );

				if ( tabindex ) {
					return parseInt( tabindex, 10 );
				}

				if (
					rfocusable.test( elem.nodeName ) ||

					// href-less anchor's `tabIndex` property value is `0` and
					// the `tabindex` attribute value: `null`. We want `-1`.
					rclickable.test( elem.nodeName ) && elem.href
				) {
					return 0;
				}

				return -1;
			}
		}
	},

	propFix: {
		"for": "htmlFor",
		"class": "className"
	}
} );

// Support: IE <=11+
// Accessing the selectedIndex property forces the browser to respect
// setting selected on the option. The getter ensures a default option
// is selected when in an optgroup. ESLint rule "no-unused-expressions"
// is disabled for this code since it considers such accessions noop.
if ( isIE ) {
	jQuery.propHooks.selected = {
		get: function( elem ) {

			var parent = elem.parentNode;
			if ( parent && parent.parentNode ) {
				// eslint-disable-next-line no-unused-expressions
				parent.parentNode.selectedIndex;
			}
			return null;
		},
		set: function( elem ) {


			var parent = elem.parentNode;
			if ( parent ) {
				// eslint-disable-next-line no-unused-expressions
				parent.selectedIndex;

				if ( parent.parentNode ) {
					// eslint-disable-next-line no-unused-expressions
					parent.parentNode.selectedIndex;
				}
			}
		}
	};
}

jQuery.each( [
	"tabIndex",
	"readOnly",
	"maxLength",
	"cellSpacing",
	"cellPadding",
	"rowSpan",
	"colSpan",
	"useMap",
	"frameBorder",
	"contentEditable"
], function() {
	jQuery.propFix[ this.toLowerCase() ] = this;
} );

// Strip and collapse whitespace according to HTML spec
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
function stripAndCollapse( value ) {
	var tokens = value.match( rnothtmlwhite ) || [];
	return tokens.join( " " );
}

function getClass( elem ) {
	return elem.getAttribute && elem.getAttribute( "class" ) || "";
}

function classesToArray( value ) {
	if ( Array.isArray( value ) ) {
		return value;
	}
	if ( typeof value === "string" ) {
		return value.match( rnothtmlwhite ) || [];
	}
	return [];
}

jQuery.fn.extend( {
	addClass: function( value ) {
		var classNames, cur, curValue, className, i, finalValue;

		if ( typeof value === "function" ) {
			return this.each( function( j ) {
				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
			} );
		}

		classNames = classesToArray( value );

		if ( classNames.length ) {
			return this.each( function() {
				curValue = getClass( this );
				cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

				if ( cur ) {
					for ( i = 0; i < classNames.length; i++ ) {
						className = classNames[ i ];
						if ( cur.indexOf( " " + className + " " ) < 0 ) {
							cur += className + " ";
						}
					}

					// Only assign if different to avoid unneeded rendering.
					finalValue = stripAndCollapse( cur );
					if ( curValue !== finalValue ) {
						this.setAttribute( "class", finalValue );
					}
				}
			} );
		}

		return this;
	},

	removeClass: function( value ) {
		var classNames, cur, curValue, className, i, finalValue;

		if ( typeof value === "function" ) {
			return this.each( function( j ) {
				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
			} );
		}

		if ( !arguments.length ) {
			return this.attr( "class", "" );
		}

		classNames = classesToArray( value );

		if ( classNames.length ) {
			return this.each( function() {
				curValue = getClass( this );

				// This expression is here for better compressibility (see addClass)
				cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

				if ( cur ) {
					for ( i = 0; i < classNames.length; i++ ) {
						className = classNames[ i ];

						// Remove *all* instances
						while ( cur.indexOf( " " + className + " " ) > -1 ) {
							cur = cur.replace( " " + className + " ", " " );
						}
					}

					// Only assign if different to avoid unneeded rendering.
					finalValue = stripAndCollapse( cur );
					if ( curValue !== finalValue ) {
						this.setAttribute( "class", finalValue );
					}
				}
			} );
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var classNames, className, i, self;

		if ( typeof value === "function" ) {
			return this.each( function( i ) {
				jQuery( this ).toggleClass(
					value.call( this, i, getClass( this ), stateVal ),
					stateVal
				);
			} );
		}

		if ( typeof stateVal === "boolean" ) {
			return stateVal ? this.addClass( value ) : this.removeClass( value );
		}

		classNames = classesToArray( value );

		if ( classNames.length ) {
			return this.each( function() {

				// Toggle individual class names
				self = jQuery( this );

				for ( i = 0; i < classNames.length; i++ ) {
					className = classNames[ i ];

					// Check each className given, space separated list
					if ( self.hasClass( className ) ) {
						self.removeClass( className );
					} else {
						self.addClass( className );
					}
				}
			} );
		}

		return this;
	},

	hasClass: function( selector ) {
		var className, elem,
			i = 0;

		className = " " + selector + " ";
		while ( ( elem = this[ i++ ] ) ) {
			if ( elem.nodeType === 1 &&
				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
				return true;
			}
		}

		return false;
	}
} );

jQuery.fn.extend( {
	val: function( value ) {
		var hooks, ret, valueIsFunction,
			elem = this[ 0 ];

		if ( !arguments.length ) {
			if ( elem ) {
				hooks = jQuery.valHooks[ elem.type ] ||
					jQuery.valHooks[ elem.nodeName.toLowerCase() ];

				if ( hooks &&
					"get" in hooks &&
					( ret = hooks.get( elem, "value" ) ) !== undefined
				) {
					return ret;
				}

				ret = elem.value;

				// Handle cases where value is null/undef or number
				return ret == null ? "" : ret;
			}

			return;
		}

		valueIsFunction = typeof value === "function";

		return this.each( function( i ) {
			var val;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( valueIsFunction ) {
				val = value.call( this, i, jQuery( this ).val() );
			} else {
				val = value;
			}

			// Treat null/undefined as ""; convert numbers to string
			if ( val == null ) {
				val = "";

			} else if ( typeof val === "number" ) {
				val += "";

			} else if ( Array.isArray( val ) ) {
				val = jQuery.map( val, function( value ) {
					return value == null ? "" : value + "";
				} );
			}

			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

			// If set returns undefined, fall back to normal setting
			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
				this.value = val;
			}
		} );
	}
} );

jQuery.extend( {
	valHooks: {
		select: {
			get: function( elem ) {
				var value, option, i,
					options = elem.options,
					index = elem.selectedIndex,
					one = elem.type === "select-one",
					values = one ? null : [],
					max = one ? index + 1 : options.length;

				if ( index < 0 ) {
					i = max;

				} else {
					i = one ? index : 0;
				}

				// Loop through all the selected options
				for ( ; i < max; i++ ) {
					option = options[ i ];

					if ( option.selected &&

							// Don't return options that are disabled or in a disabled optgroup
							!option.disabled &&
							( !option.parentNode.disabled ||
								!nodeName( option.parentNode, "optgroup" ) ) ) {

						// Get the specific value for the option
						value = jQuery( option ).val();

						// We don't need an array for one selects
						if ( one ) {
							return value;
						}

						// Multi-Selects return an array
						values.push( value );
					}
				}

				return values;
			},

			set: function( elem, value ) {
				var optionSet, option,
					options = elem.options,
					values = jQuery.makeArray( value ),
					i = options.length;

				while ( i-- ) {
					option = options[ i ];

					if ( ( option.selected =
						jQuery.inArray( jQuery( option ).val(), values ) > -1
					) ) {
						optionSet = true;
					}
				}

				// Force browsers to behave consistently when non-matching value is set
				if ( !optionSet ) {
					elem.selectedIndex = -1;
				}
				return values;
			}
		}
	}
} );

if ( isIE ) {
	jQuery.valHooks.option = {
		get: function( elem ) {

			var val = elem.getAttribute( "value" );
			return val != null ?
				val :

				// Support: IE <=10 - 11+
				// option.text throws exceptions (trac-14686, trac-14858)
				// Strip and collapse whitespace
				// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
				stripAndCollapse( jQuery.text( elem ) );
		}
	};
}

// Radios and checkboxes getter/setter
jQuery.each( [ "radio", "checkbox" ], function() {
	jQuery.valHooks[ this ] = {
		set: function( elem, value ) {
			if ( Array.isArray( value ) ) {
				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
			}
		}
	};
} );

var rcheckableType = /^(?:checkbox|radio)$/i;

var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

function returnTrue() {
	return true;
}

function returnFalse() {
	return false;
}

function on( elem, types, selector, data, fn, one ) {
	var origFn, type;

	// Types can be a map of types/handlers
	if ( typeof types === "object" ) {

		// ( types-Object, selector, data )
		if ( typeof selector !== "string" ) {

			// ( types-Object, data )
			data = data || selector;
			selector = undefined;
		}
		for ( type in types ) {
			on( elem, type, selector, data, types[ type ], one );
		}
		return elem;
	}

	if ( data == null && fn == null ) {

		// ( types, fn )
		fn = selector;
		data = selector = undefined;
	} else if ( fn == null ) {
		if ( typeof selector === "string" ) {

			// ( types, selector, fn )
			fn = data;
			data = undefined;
		} else {

			// ( types, data, fn )
			fn = data;
			data = selector;
			selector = undefined;
		}
	}
	if ( fn === false ) {
		fn = returnFalse;
	} else if ( !fn ) {
		return elem;
	}

	if ( one === 1 ) {
		origFn = fn;
		fn = function( event ) {

			// Can use an empty set, since event contains the info
			jQuery().off( event );
			return origFn.apply( this, arguments );
		};

		// Use same guid so caller can remove using origFn
		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
	}
	return elem.each( function() {
		jQuery.event.add( this, types, fn, data, selector );
	} );
}

/*
 * Helper functions for managing events -- not part of the public interface.
 * Props to Dean Edwards' addEvent library for many of the ideas.
 */
jQuery.event = {

	add: function( elem, types, handler, data, selector ) {

		var handleObjIn, eventHandle, tmp,
			events, t, handleObj,
			special, handlers, type, namespaces, origType,
			elemData = dataPriv.get( elem );

		// Only attach events to objects that accept data
		if ( !acceptData( elem ) ) {
			return;
		}

		// Caller can pass in an object of custom data in lieu of the handler
		if ( handler.handler ) {
			handleObjIn = handler;
			handler = handleObjIn.handler;
			selector = handleObjIn.selector;
		}

		// Ensure that invalid selectors throw exceptions at attach time
		// Evaluate against documentElement in case elem is a non-element node (e.g., document)
		if ( selector ) {
			jQuery.find.matchesSelector( documentElement$1, selector );
		}

		// Make sure that the handler has a unique ID, used to find/remove it later
		if ( !handler.guid ) {
			handler.guid = jQuery.guid++;
		}

		// Init the element's event structure and main handler, if this is the first
		if ( !( events = elemData.events ) ) {
			events = elemData.events = Object.create( null );
		}
		if ( !( eventHandle = elemData.handle ) ) {
			eventHandle = elemData.handle = function( e ) {

				// Discard the second event of a jQuery.event.trigger() and
				// when an event is called after a page has unloaded
				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
			};
		}

		// Handle multiple events separated by a space
		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[ t ] ) || [];
			type = origType = tmp[ 1 ];
			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

			// There *must* be a type, no attaching namespace-only handlers
			if ( !type ) {
				continue;
			}

			// If event changes its type, use the special event handlers for the changed type
			special = jQuery.event.special[ type ] || {};

			// If selector defined, determine special event api type, otherwise given type
			type = ( selector ? special.delegateType : special.bindType ) || type;

			// Update special based on newly reset type
			special = jQuery.event.special[ type ] || {};

			// handleObj is passed to all event handlers
			handleObj = jQuery.extend( {
				type: type,
				origType: origType,
				data: data,
				handler: handler,
				guid: handler.guid,
				selector: selector,
				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
				namespace: namespaces.join( "." )
			}, handleObjIn );

			// Init the event handler queue if we're the first
			if ( !( handlers = events[ type ] ) ) {
				handlers = events[ type ] = [];
				handlers.delegateCount = 0;

				// Only use addEventListener if the special events handler returns false
				if ( !special.setup ||
					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {

					if ( elem.addEventListener ) {
						elem.addEventListener( type, eventHandle );
					}
				}
			}

			if ( special.add ) {
				special.add.call( elem, handleObj );

				if ( !handleObj.handler.guid ) {
					handleObj.handler.guid = handler.guid;
				}
			}

			// Add to the element's handler list, delegates in front
			if ( selector ) {
				handlers.splice( handlers.delegateCount++, 0, handleObj );
			} else {
				handlers.push( handleObj );
			}
		}

	},

	// Detach an event or set of events from an element
	remove: function( elem, types, handler, selector, mappedTypes ) {

		var j, origCount, tmp,
			events, t, handleObj,
			special, handlers, type, namespaces, origType,
			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );

		if ( !elemData || !( events = elemData.events ) ) {
			return;
		}

		// Once for each type.namespace in types; type may be omitted
		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[ t ] ) || [];
			type = origType = tmp[ 1 ];
			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

			// Unbind all events (on this namespace, if provided) for the element
			if ( !type ) {
				for ( type in events ) {
					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
				}
				continue;
			}

			special = jQuery.event.special[ type ] || {};
			type = ( selector ? special.delegateType : special.bindType ) || type;
			handlers = events[ type ] || [];
			tmp = tmp[ 2 ] &&
				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );

			// Remove matching events
			origCount = j = handlers.length;
			while ( j-- ) {
				handleObj = handlers[ j ];

				if ( ( mappedTypes || origType === handleObj.origType ) &&
					( !handler || handler.guid === handleObj.guid ) &&
					( !tmp || tmp.test( handleObj.namespace ) ) &&
					( !selector || selector === handleObj.selector ||
						selector === "**" && handleObj.selector ) ) {
					handlers.splice( j, 1 );

					if ( handleObj.selector ) {
						handlers.delegateCount--;
					}
					if ( special.remove ) {
						special.remove.call( elem, handleObj );
					}
				}
			}

			// Remove generic event handler if we removed something and no more handlers exist
			// (avoids potential for endless recursion during removal of special event handlers)
			if ( origCount && !handlers.length ) {
				if ( !special.teardown ||
					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {

					jQuery.removeEvent( elem, type, elemData.handle );
				}

				delete events[ type ];
			}
		}

		// Remove data and the expando if it's no longer used
		if ( jQuery.isEmptyObject( events ) ) {
			dataPriv.remove( elem, "handle events" );
		}
	},

	dispatch: function( nativeEvent ) {

		var i, j, ret, matched, handleObj, handlerQueue,
			args = new Array( arguments.length ),

			// Make a writable jQuery.Event from the native event object
			event = jQuery.event.fix( nativeEvent ),

			handlers = (
				dataPriv.get( this, "events" ) || Object.create( null )
			)[ event.type ] || [],
			special = jQuery.event.special[ event.type ] || {};

		// Use the fix-ed jQuery.Event rather than the (read-only) native event
		args[ 0 ] = event;

		for ( i = 1; i < arguments.length; i++ ) {
			args[ i ] = arguments[ i ];
		}

		event.delegateTarget = this;

		// Call the preDispatch hook for the mapped type, and let it bail if desired
		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
			return;
		}

		// Determine handlers
		handlerQueue = jQuery.event.handlers.call( this, event, handlers );

		// Run delegates first; they may want to stop propagation beneath us
		i = 0;
		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
			event.currentTarget = matched.elem;

			j = 0;
			while ( ( handleObj = matched.handlers[ j++ ] ) &&
				!event.isImmediatePropagationStopped() ) {

				// If the event is namespaced, then each handler is only invoked if it is
				// specially universal or its namespaces are a superset of the event's.
				if ( !event.rnamespace || handleObj.namespace === false ||
					event.rnamespace.test( handleObj.namespace ) ) {

					event.handleObj = handleObj;
					event.data = handleObj.data;

					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
						handleObj.handler ).apply( matched.elem, args );

					if ( ret !== undefined ) {
						if ( ( event.result = ret ) === false ) {
							event.preventDefault();
							event.stopPropagation();
						}
					}
				}
			}
		}

		// Call the postDispatch hook for the mapped type
		if ( special.postDispatch ) {
			special.postDispatch.call( this, event );
		}

		return event.result;
	},

	handlers: function( event, handlers ) {
		var i, handleObj, sel, matchedHandlers, matchedSelectors,
			handlerQueue = [],
			delegateCount = handlers.delegateCount,
			cur = event.target;

		// Find delegate handlers
		if ( delegateCount &&

			// Support: Firefox <=42 - 66+
			// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
			// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
			// Support: IE 11+
			// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
			!( event.type === "click" && event.button >= 1 ) ) {

			for ( ; cur !== this; cur = cur.parentNode || this ) {

				// Don't check non-elements (trac-13208)
				// Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764)
				if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
					matchedHandlers = [];
					matchedSelectors = {};
					for ( i = 0; i < delegateCount; i++ ) {
						handleObj = handlers[ i ];

						// Don't conflict with Object.prototype properties (trac-13203)
						sel = handleObj.selector + " ";

						if ( matchedSelectors[ sel ] === undefined ) {
							matchedSelectors[ sel ] = handleObj.needsContext ?
								jQuery( sel, this ).index( cur ) > -1 :
								jQuery.find( sel, this, null, [ cur ] ).length;
						}
						if ( matchedSelectors[ sel ] ) {
							matchedHandlers.push( handleObj );
						}
					}
					if ( matchedHandlers.length ) {
						handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
					}
				}
			}
		}

		// Add the remaining (directly-bound) handlers
		cur = this;
		if ( delegateCount < handlers.length ) {
			handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
		}

		return handlerQueue;
	},

	addProp: function( name, hook ) {
		Object.defineProperty( jQuery.Event.prototype, name, {
			enumerable: true,
			configurable: true,

			get: typeof hook === "function" ?
				function() {
					if ( this.originalEvent ) {
						return hook( this.originalEvent );
					}
				} :
				function() {
					if ( this.originalEvent ) {
						return this.originalEvent[ name ];
					}
				},

			set: function( value ) {
				Object.defineProperty( this, name, {
					enumerable: true,
					configurable: true,
					writable: true,
					value: value
				} );
			}
		} );
	},

	fix: function( originalEvent ) {
		return originalEvent[ jQuery.expando ] ?
			originalEvent :
			new jQuery.Event( originalEvent );
	},

	special: jQuery.extend( Object.create( null ), {
		load: {

			// Prevent triggered image.load events from bubbling to window.load
			noBubble: true
		},
		click: {

			// Utilize native event to ensure correct state for checkable inputs
			setup: function( data ) {

				// For mutual compressibility with _default, replace `this` access with a local var.
				// `|| data` is dead code meant only to preserve the variable through minification.
				var el = this || data;

				// Claim the first handler
				if ( rcheckableType.test( el.type ) &&
					el.click && nodeName( el, "input" ) ) {

					// dataPriv.set( el, "click", ... )
					leverageNative( el, "click", true );
				}

				// Return false to allow normal processing in the caller
				return false;
			},
			trigger: function( data ) {

				// For mutual compressibility with _default, replace `this` access with a local var.
				// `|| data` is dead code meant only to preserve the variable through minification.
				var el = this || data;

				// Force setup before triggering a click
				if ( rcheckableType.test( el.type ) &&
					el.click && nodeName( el, "input" ) ) {

					leverageNative( el, "click" );
				}

				// Return non-false to allow normal event-path propagation
				return true;
			},

			// For cross-browser consistency, suppress native .click() on links
			// Also prevent it if we're currently inside a leveraged native-event stack
			_default: function( event ) {
				var target = event.target;
				return rcheckableType.test( target.type ) &&
					target.click && nodeName( target, "input" ) &&
					dataPriv.get( target, "click" ) ||
					nodeName( target, "a" );
			}
		},

		beforeunload: {
			postDispatch: function( event ) {
				if ( event.result !== undefined ) {

					// Setting `event.originalEvent.returnValue` in modern
					// browsers does the same as just calling `preventDefault()`,
					// the browsers ignore the value anyway.
					// Incidentally, IE 11 is the only browser from our supported
					// ones which respects the value returned from a `beforeunload`
					// handler attached by `addEventListener`; other browsers do
					// so only for inline handlers, so not setting the value
					// directly shouldn't reduce any functionality.
					event.preventDefault();
				}
			}
		}
	} )
};

// Ensure the presence of an event listener that handles manually-triggered
// synthetic events by interrupting progress until reinvoked in response to
// *native* events that it fires directly, ensuring that state changes have
// already occurred before other listeners are invoked.
function leverageNative( el, type, isSetup ) {

	// Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add
	if ( !isSetup ) {
		if ( dataPriv.get( el, type ) === undefined ) {
			jQuery.event.add( el, type, returnTrue );
		}
		return;
	}

	// Register the controller as a special universal handler for all event namespaces
	dataPriv.set( el, type, false );
	jQuery.event.add( el, type, {
		namespace: false,
		handler: function( event ) {
			var result,
				saved = dataPriv.get( this, type );

			// This controller function is invoked under multiple circumstances,
			// differentiated by the stored value in `saved`:
			// 1. For an outer synthetic `.trigger()`ed event (detected by
			//    `event.isTrigger & 1` and non-array `saved`), it records arguments
			//    as an array and fires an [inner] native event to prompt state
			//    changes that should be observed by registered listeners (such as
			//    checkbox toggling and focus updating), then clears the stored value.
			// 2. For an [inner] native event (detected by `saved` being
			//    an array), it triggers an inner synthetic event, records the
			//    result, and preempts propagation to further jQuery listeners.
			// 3. For an inner synthetic event (detected by `event.isTrigger & 1` and
			//    array `saved`), it prevents double-propagation of surrogate events
			//    but otherwise allows everything to proceed (particularly including
			//    further listeners).
			// Possible `saved` data shapes: `[...], `{ value }`, `false`.
			if ( ( event.isTrigger & 1 ) && this[ type ] ) {

				// Interrupt processing of the outer synthetic .trigger()ed event
				if ( !saved.length ) {

					// Store arguments for use when handling the inner native event
					// There will always be at least one argument (an event object),
					// so this array will not be confused with a leftover capture object.
					saved = slice.call( arguments );
					dataPriv.set( this, type, saved );

					// Trigger the native event and capture its result
					this[ type ]();
					result = dataPriv.get( this, type );
					dataPriv.set( this, type, false );

					if ( saved !== result ) {

						// Cancel the outer synthetic event
						event.stopImmediatePropagation();
						event.preventDefault();

						// Support: Chrome 86+
						// In Chrome, if an element having a focusout handler is
						// blurred by clicking outside of it, it invokes the handler
						// synchronously. If that handler calls `.remove()` on
						// the element, the data is cleared, leaving `result`
						// undefined. We need to guard against this.
						return result && result.value;
					}

				// If this is an inner synthetic event for an event with a bubbling
				// surrogate (focus or blur), assume that the surrogate already
				// propagated from triggering the native event and prevent that
				// from happening again here.
				} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
					event.stopPropagation();
				}

			// If this is a native event triggered above, everything is now in order.
			// Fire an inner synthetic event with the original arguments.
			} else if ( saved.length ) {

				// ...and capture the result
				dataPriv.set( this, type, {
					value: jQuery.event.trigger(
						saved[ 0 ],
						saved.slice( 1 ),
						this
					)
				} );

				// Abort handling of the native event by all jQuery handlers while allowing
				// native handlers on the same element to run. On target, this is achieved
				// by stopping immediate propagation just on the jQuery event. However,
				// the native event is re-wrapped by a jQuery one on each level of the
				// propagation so the only way to stop it for jQuery is to stop it for
				// everyone via native `stopPropagation()`. This is not a problem for
				// focus/blur which don't bubble, but it does also stop click on checkboxes
				// and radios. We accept this limitation.
				event.stopPropagation();
				event.isImmediatePropagationStopped = returnTrue;
			}
		}
	} );
}

jQuery.removeEvent = function( elem, type, handle ) {

	// This "if" is needed for plain objects
	if ( elem.removeEventListener ) {
		elem.removeEventListener( type, handle );
	}
};

jQuery.Event = function( src, props ) {

	// Allow instantiation without the 'new' keyword
	if ( !( this instanceof jQuery.Event ) ) {
		return new jQuery.Event( src, props );
	}

	// Event object
	if ( src && src.type ) {
		this.originalEvent = src;
		this.type = src.type;

		// Events bubbling up the document may have been marked as prevented
		// by a handler lower down the tree; reflect the correct value.
		this.isDefaultPrevented = src.defaultPrevented ?
			returnTrue :
			returnFalse;

		// Create target properties
		this.target = src.target;
		this.currentTarget = src.currentTarget;
		this.relatedTarget = src.relatedTarget;

	// Event type
	} else {
		this.type = src;
	}

	// Put explicitly provided properties onto the event object
	if ( props ) {
		jQuery.extend( this, props );
	}

	// Create a timestamp if incoming event doesn't have one
	this.timeStamp = src && src.timeStamp || Date.now();

	// Mark it as fixed
	this[ jQuery.expando ] = true;
};

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	constructor: jQuery.Event,
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse,
	isSimulated: false,

	preventDefault: function() {
		var e = this.originalEvent;

		this.isDefaultPrevented = returnTrue;

		if ( e && !this.isSimulated ) {
			e.preventDefault();
		}
	},
	stopPropagation: function() {
		var e = this.originalEvent;

		this.isPropagationStopped = returnTrue;

		if ( e && !this.isSimulated ) {
			e.stopPropagation();
		}
	},
	stopImmediatePropagation: function() {
		var e = this.originalEvent;

		this.isImmediatePropagationStopped = returnTrue;

		if ( e && !this.isSimulated ) {
			e.stopImmediatePropagation();
		}

		this.stopPropagation();
	}
};

// Includes all common event props including KeyEvent and MouseEvent specific props
jQuery.each( {
	altKey: true,
	bubbles: true,
	cancelable: true,
	changedTouches: true,
	ctrlKey: true,
	detail: true,
	eventPhase: true,
	metaKey: true,
	pageX: true,
	pageY: true,
	shiftKey: true,
	view: true,
	"char": true,
	code: true,
	charCode: true,
	key: true,
	keyCode: true,
	button: true,
	buttons: true,
	clientX: true,
	clientY: true,
	offsetX: true,
	offsetY: true,
	pointerId: true,
	pointerType: true,
	screenX: true,
	screenY: true,
	targetTouches: true,
	toElement: true,
	touches: true,
	which: true
}, jQuery.event.addProp );

jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {

	// Support: IE 11+
	// Attach a single focusin/focusout handler on the document while someone wants focus/blur.
	// This is because the former are synchronous in IE while the latter are async. In other
	// browsers, all those handlers are invoked synchronously.
	function focusMappedHandler( nativeEvent ) {

		// `eventHandle` would already wrap the event, but we need to change the `type` here.
		var event = jQuery.event.fix( nativeEvent );
		event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
		event.isSimulated = true;

		// focus/blur don't bubble while focusin/focusout do; simulate the former by only
		// invoking the handler at the lower level.
		if ( event.target === event.currentTarget ) {

			// The setup part calls `leverageNative`, which, in turn, calls
			// `jQuery.event.add`, so event handle will already have been set
			// by this point.
			dataPriv.get( this, "handle" )( event );
		}
	}

	jQuery.event.special[ type ] = {

		// Utilize native event if possible so blur/focus sequence is correct
		setup: function() {

			// Claim the first handler
			// dataPriv.set( this, "focus", ... )
			// dataPriv.set( this, "blur", ... )
			leverageNative( this, type, true );

			if ( isIE ) {
				this.addEventListener( delegateType, focusMappedHandler );
			} else {

				// Return false to allow normal processing in the caller
				return false;
			}
		},
		trigger: function() {

			// Force setup before trigger
			leverageNative( this, type );

			// Return non-false to allow normal event-path propagation
			return true;
		},

		teardown: function() {
			if ( isIE ) {
				this.removeEventListener( delegateType, focusMappedHandler );
			} else {

				// Return false to indicate standard teardown should be applied
				return false;
			}
		},

		// Suppress native focus or blur if we're currently inside
		// a leveraged native-event stack
		_default: function( event ) {
			return dataPriv.get( event.target, type );
		},

		delegateType: delegateType
	};
} );

// Create mouseenter/leave events using mouseover/out and event-time checks
// so that event delegation works in jQuery.
// Do the same for pointerenter/pointerleave and pointerover/pointerout
jQuery.each( {
	mouseenter: "mouseover",
	mouseleave: "mouseout",
	pointerenter: "pointerover",
	pointerleave: "pointerout"
}, function( orig, fix ) {
	jQuery.event.special[ orig ] = {
		delegateType: fix,
		bindType: fix,

		handle: function( event ) {
			var ret,
				target = this,
				related = event.relatedTarget,
				handleObj = event.handleObj;

			// For mouseenter/leave call the handler if related is outside the target.
			// NB: No relatedTarget if the mouse left/entered the browser window
			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
				event.type = handleObj.origType;
				ret = handleObj.handler.apply( this, arguments );
				event.type = fix;
			}
			return ret;
		}
	};
} );

jQuery.fn.extend( {

	on: function( types, selector, data, fn ) {
		return on( this, types, selector, data, fn );
	},
	one: function( types, selector, data, fn ) {
		return on( this, types, selector, data, fn, 1 );
	},
	off: function( types, selector, fn ) {
		var handleObj, type;
		if ( types && types.preventDefault && types.handleObj ) {

			// ( event )  dispatched jQuery.Event
			handleObj = types.handleObj;
			jQuery( types.delegateTarget ).off(
				handleObj.namespace ?
					handleObj.origType + "." + handleObj.namespace :
					handleObj.origType,
				handleObj.selector,
				handleObj.handler
			);
			return this;
		}
		if ( typeof types === "object" ) {

			// ( types-object [, selector] )
			for ( type in types ) {
				this.off( type, selector, types[ type ] );
			}
			return this;
		}
		if ( selector === false || typeof selector === "function" ) {

			// ( types [, fn] )
			fn = selector;
			selector = undefined;
		}
		if ( fn === false ) {
			fn = returnFalse;
		}
		return this.each( function() {
			jQuery.event.remove( this, types, fn, selector );
		} );
	}
} );

var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
	stopPropagationCallback = function( e ) {
		e.stopPropagation();
	};

jQuery.extend( jQuery.event, {

	trigger: function( event, data, elem, onlyHandlers ) {

		var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
			eventPath = [ elem || document$1 ],
			type = hasOwn.call( event, "type" ) ? event.type : event,
			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];

		cur = lastElement = tmp = elem = elem || document$1;

		// Don't do events on text and comment nodes
		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
			return;
		}

		// focus/blur morphs to focusin/out; ensure we're not firing them right now
		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
			return;
		}

		if ( type.indexOf( "." ) > -1 ) {

			// Namespaced trigger; create a regexp to match event type in handle()
			namespaces = type.split( "." );
			type = namespaces.shift();
			namespaces.sort();
		}
		ontype = type.indexOf( ":" ) < 0 && "on" + type;

		// Caller can pass in a jQuery.Event object, Object, or just an event type string
		event = event[ jQuery.expando ] ?
			event :
			new jQuery.Event( type, typeof event === "object" && event );

		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
		event.isTrigger = onlyHandlers ? 2 : 3;
		event.namespace = namespaces.join( "." );
		event.rnamespace = event.namespace ?
			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
			null;

		// Clean up the event in case it is being reused
		event.result = undefined;
		if ( !event.target ) {
			event.target = elem;
		}

		// Clone any incoming data and prepend the event, creating the handler arg list
		data = data == null ?
			[ event ] :
			jQuery.makeArray( data, [ event ] );

		// Allow special events to draw outside the lines
		special = jQuery.event.special[ type ] || {};
		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
			return;
		}

		// Determine event propagation path in advance, per W3C events spec (trac-9951)
		// Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724)
		if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {

			bubbleType = special.delegateType || type;
			if ( !rfocusMorph.test( bubbleType + type ) ) {
				cur = cur.parentNode;
			}
			for ( ; cur; cur = cur.parentNode ) {
				eventPath.push( cur );
				tmp = cur;
			}

			// Only add window if we got to document (e.g., not plain obj or detached DOM)
			if ( tmp === ( elem.ownerDocument || document$1 ) ) {
				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
			}
		}

		// Fire handlers on the event path
		i = 0;
		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
			lastElement = cur;
			event.type = i > 1 ?
				bubbleType :
				special.bindType || type;

			// jQuery handler
			handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] &&
				dataPriv.get( cur, "handle" );
			if ( handle ) {
				handle.apply( cur, data );
			}

			// Native handler
			handle = ontype && cur[ ontype ];
			if ( handle && handle.apply && acceptData( cur ) ) {
				event.result = handle.apply( cur, data );
				if ( event.result === false ) {
					event.preventDefault();
				}
			}
		}
		event.type = type;

		// If nobody prevented the default action, do it now
		if ( !onlyHandlers && !event.isDefaultPrevented() ) {

			if ( ( !special._default ||
				special._default.apply( eventPath.pop(), data ) === false ) &&
				acceptData( elem ) ) {

				// Call a native DOM method on the target with the same name as the event.
				// Don't do default actions on window, that's where global variables be (trac-6170)
				if ( ontype && typeof elem[ type ] === "function" && !isWindow( elem ) ) {

					// Don't re-trigger an onFOO event when we call its FOO() method
					tmp = elem[ ontype ];

					if ( tmp ) {
						elem[ ontype ] = null;
					}

					// Prevent re-triggering of the same event, since we already bubbled it above
					jQuery.event.triggered = type;

					if ( event.isPropagationStopped() ) {
						lastElement.addEventListener( type, stopPropagationCallback );
					}

					elem[ type ]();

					if ( event.isPropagationStopped() ) {
						lastElement.removeEventListener( type, stopPropagationCallback );
					}

					jQuery.event.triggered = undefined;

					if ( tmp ) {
						elem[ ontype ] = tmp;
					}
				}
			}
		}

		return event.result;
	},

	// Piggyback on a donor event to simulate a different one
	// Used only for `focus(in | out)` events
	simulate: function( type, elem, event ) {
		var e = jQuery.extend(
			new jQuery.Event(),
			event,
			{
				type: type,
				isSimulated: true
			}
		);

		jQuery.event.trigger( e, null, elem );
	}

} );

jQuery.fn.extend( {

	trigger: function( type, data ) {
		return this.each( function() {
			jQuery.event.trigger( type, data, this );
		} );
	},
	triggerHandler: function( type, data ) {
		var elem = this[ 0 ];
		if ( elem ) {
			return jQuery.event.trigger( type, data, elem, true );
		}
	}
} );

var isAttached = function( elem ) {
		return jQuery.contains( elem.ownerDocument, elem ) ||
			elem.getRootNode( composed ) === elem.ownerDocument;
	},
	composed = { composed: true };

// Support: IE 9 - 11+
// Check attachment across shadow DOM boundaries when possible (gh-3504).
// Provide a fallback for browsers without Shadow DOM v1 support.
if ( !documentElement$1.getRootNode ) {
	isAttached = function( elem ) {
		return jQuery.contains( elem.ownerDocument, elem );
	};
}

// rtagName captures the name from the first start tag in a string of HTML
// https://html.spec.whatwg.org/multipage/syntax.html#tag-open-state
// https://html.spec.whatwg.org/multipage/syntax.html#tag-name-state
var rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i;

var wrapMap = {

	// Table parts need to be wrapped with `<table>` or they're
	// stripped to their contents when put in a div.
	// XHTML parsers do not magically insert elements in the
	// same way that tag soup parsers do, so we cannot shorten
	// this by omitting <tbody> or other required elements.
	thead: [ "table" ],
	col: [ "colgroup", "table" ],
	tr: [ "tbody", "table" ],
	td: [ "tr", "tbody", "table" ]
};

wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;

function getAll( context, tag ) {

	// Support: IE <=9 - 11+
	// Use typeof to avoid zero-argument method invocation on host objects (trac-15151)
	var ret;

	if ( typeof context.getElementsByTagName !== "undefined" ) {

		// Use slice to snapshot the live collection from gEBTN
		ret = arr.slice.call( context.getElementsByTagName( tag || "*" ) );

	} else if ( typeof context.querySelectorAll !== "undefined" ) {
		ret = context.querySelectorAll( tag || "*" );

	} else {
		ret = [];
	}

	if ( tag === undefined || tag && nodeName( context, tag ) ) {
		return jQuery.merge( [ context ], ret );
	}

	return ret;
}

var rscriptType = /^$|^module$|\/(?:java|ecma)script/i;

// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
	var i = 0,
		l = elems.length;

	for ( ; i < l; i++ ) {
		dataPriv.set(
			elems[ i ],
			"globalEval",
			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
		);
	}
}

var rhtml = /<|&#?\w+;/;

function buildFragment( elems, context, scripts, selection, ignored ) {
	var elem, tmp, tag, wrap, attached, j,
		fragment = context.createDocumentFragment(),
		nodes = [],
		i = 0,
		l = elems.length;

	for ( ; i < l; i++ ) {
		elem = elems[ i ];

		if ( elem || elem === 0 ) {

			// Add nodes directly
			if ( toType( elem ) === "object" && ( elem.nodeType || isArrayLike( elem ) ) ) {
				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );

			// Convert non-html into a text node
			} else if ( !rhtml.test( elem ) ) {
				nodes.push( context.createTextNode( elem ) );

			// Convert html into DOM nodes
			} else {
				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );

				// Deserialize a standard representation
				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
				wrap = wrapMap[ tag ] || arr;

				// Create wrappers & descend into them.
				j = wrap.length;
				while ( --j > -1 ) {
					tmp = tmp.appendChild( context.createElement( wrap[ j ] ) );
				}

				tmp.innerHTML = jQuery.htmlPrefilter( elem );

				jQuery.merge( nodes, tmp.childNodes );

				// Remember the top-level container
				tmp = fragment.firstChild;

				// Ensure the created nodes are orphaned (trac-12392)
				tmp.textContent = "";
			}
		}
	}

	// Remove wrapper from fragment
	fragment.textContent = "";

	i = 0;
	while ( ( elem = nodes[ i++ ] ) ) {

		// Skip elements already in the context collection (trac-4087)
		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
			if ( ignored ) {
				ignored.push( elem );
			}
			continue;
		}

		attached = isAttached( elem );

		// Append to fragment
		tmp = getAll( fragment.appendChild( elem ), "script" );

		// Preserve script evaluation history
		if ( attached ) {
			setGlobalEval( tmp );
		}

		// Capture executables
		if ( scripts ) {
			j = 0;
			while ( ( elem = tmp[ j++ ] ) ) {
				if ( rscriptType.test( elem.type || "" ) ) {
					scripts.push( elem );
				}
			}
		}
	}

	return fragment;
}

// Replace/restore the type attribute of script elements for safe DOM manipulation
function disableScript( elem ) {
	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
	return elem;
}
function restoreScript( elem ) {
	if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
		elem.type = elem.type.slice( 5 );
	} else {
		elem.removeAttribute( "type" );
	}

	return elem;
}

function domManip( collection, args, callback, ignored ) {

	// Flatten any nested arrays
	args = flat( args );

	var fragment, first, scripts, hasScripts, node, doc,
		i = 0,
		l = collection.length,
		iNoClone = l - 1,
		value = args[ 0 ],
		valueIsFunction = typeof value === "function";

	if ( valueIsFunction ) {
		return collection.each( function( index ) {
			var self = collection.eq( index );
			args[ 0 ] = value.call( this, index, self.html() );
			domManip( self, args, callback, ignored );
		} );
	}

	if ( l ) {
		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
		first = fragment.firstChild;

		if ( fragment.childNodes.length === 1 ) {
			fragment = first;
		}

		// Require either new content or an interest in ignored elements to invoke the callback
		if ( first || ignored ) {
			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
			hasScripts = scripts.length;

			// Use the original fragment for the last item
			// instead of the first because it can end up
			// being emptied incorrectly in certain situations (trac-8070).
			for ( ; i < l; i++ ) {
				node = fragment;

				if ( i !== iNoClone ) {
					node = jQuery.clone( node, true, true );

					// Keep references to cloned scripts for later restoration
					if ( hasScripts ) {
						jQuery.merge( scripts, getAll( node, "script" ) );
					}
				}

				callback.call( collection[ i ], node, i );
			}

			if ( hasScripts ) {
				doc = scripts[ scripts.length - 1 ].ownerDocument;

				// Re-enable scripts
				jQuery.map( scripts, restoreScript );

				// Evaluate executable scripts on first document insertion
				for ( i = 0; i < hasScripts; i++ ) {
					node = scripts[ i ];
					if ( rscriptType.test( node.type || "" ) &&
						!dataPriv.get( node, "globalEval" ) &&
						jQuery.contains( doc, node ) ) {

						if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {

							// Optional AJAX dependency, but won't run scripts if not present
							if ( jQuery._evalUrl && !node.noModule ) {
								jQuery._evalUrl( node.src, {
									nonce: node.nonce,
									crossOrigin: node.crossOrigin
								}, doc );
							}
						} else {
							DOMEval( node.textContent, node, doc );
						}
					}
				}
			}
		}
	}

	return collection;
}

var

	// Support: IE <=10 - 11+
	// In IE using regex groups here causes severe slowdowns.
	rnoInnerhtml = /<script|<style|<link/i;

// Prefer a tbody over its parent table for containing new rows
function manipulationTarget( elem, content ) {
	if ( nodeName( elem, "table" ) &&
		nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {

		return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
	}

	return elem;
}

function cloneCopyEvent( src, dest ) {
	var type, i, l,
		events = dataPriv.get( src, "events" );

	if ( dest.nodeType !== 1 ) {
		return;
	}

	// 1. Copy private data: events, handlers, etc.
	if ( events ) {
		dataPriv.remove( dest, "handle events" );
		for ( type in events ) {
			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
				jQuery.event.add( dest, type, events[ type ][ i ] );
			}
		}
	}

	// 2. Copy user data
	if ( dataUser.hasData( src ) ) {
		dataUser.set( dest, jQuery.extend( {}, dataUser.get( src ) ) );
	}
}

function remove( elem, selector, keepData ) {
	var node,
		nodes = selector ? jQuery.filter( selector, elem ) : elem,
		i = 0;

	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
		if ( !keepData && node.nodeType === 1 ) {
			jQuery.cleanData( getAll( node ) );
		}

		if ( node.parentNode ) {
			if ( keepData && isAttached( node ) ) {
				setGlobalEval( getAll( node, "script" ) );
			}
			node.parentNode.removeChild( node );
		}
	}

	return elem;
}

jQuery.extend( {
	htmlPrefilter: function( html ) {
		return html;
	},

	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
		var i, l, srcElements, destElements,
			clone = elem.cloneNode( true ),
			inPage = isAttached( elem );

		// Fix IE cloning issues
		if ( isIE && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
				!jQuery.isXMLDoc( elem ) ) {

			// We eschew jQuery#find here for performance reasons:
			// https://jsperf.com/getall-vs-sizzle/2
			destElements = getAll( clone );
			srcElements = getAll( elem );

			for ( i = 0, l = srcElements.length; i < l; i++ ) {

				// Support: IE <=11+
				// IE fails to set the defaultValue to the correct value when
				// cloning textareas.
				if ( nodeName( destElements[ i ], "textarea" ) ) {
					destElements[ i ].defaultValue = srcElements[ i ].defaultValue;
				}
			}
		}

		// Copy the events from the original to the clone
		if ( dataAndEvents ) {
			if ( deepDataAndEvents ) {
				srcElements = srcElements || getAll( elem );
				destElements = destElements || getAll( clone );

				for ( i = 0, l = srcElements.length; i < l; i++ ) {
					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
				}
			} else {
				cloneCopyEvent( elem, clone );
			}
		}

		// Preserve script evaluation history
		destElements = getAll( clone, "script" );
		if ( destElements.length > 0 ) {
			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
		}

		// Return the cloned set
		return clone;
	},

	cleanData: function( elems ) {
		var data, elem, type,
			special = jQuery.event.special,
			i = 0;

		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
			if ( acceptData( elem ) ) {
				if ( ( data = elem[ dataPriv.expando ] ) ) {
					if ( data.events ) {
						for ( type in data.events ) {
							if ( special[ type ] ) {
								jQuery.event.remove( elem, type );

							// This is a shortcut to avoid jQuery.event.remove's overhead
							} else {
								jQuery.removeEvent( elem, type, data.handle );
							}
						}
					}

					// Support: Chrome <=35 - 45+
					// Assign undefined instead of using delete, see Data#remove
					elem[ dataPriv.expando ] = undefined;
				}
				if ( elem[ dataUser.expando ] ) {

					// Support: Chrome <=35 - 45+
					// Assign undefined instead of using delete, see Data#remove
					elem[ dataUser.expando ] = undefined;
				}
			}
		}
	}
} );

jQuery.fn.extend( {
	detach: function( selector ) {
		return remove( this, selector, true );
	},

	remove: function( selector ) {
		return remove( this, selector );
	},

	text: function( value ) {
		return access( this, function( value ) {
			return value === undefined ?
				jQuery.text( this ) :
				this.empty().each( function() {
					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
						this.textContent = value;
					}
				} );
		}, null, value, arguments.length );
	},

	append: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.appendChild( elem );
			}
		} );
	},

	prepend: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.insertBefore( elem, target.firstChild );
			}
		} );
	},

	before: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this );
			}
		} );
	},

	after: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this.nextSibling );
			}
		} );
	},

	empty: function() {
		var elem,
			i = 0;

		for ( ; ( elem = this[ i ] ) != null; i++ ) {
			if ( elem.nodeType === 1 ) {

				// Prevent memory leaks
				jQuery.cleanData( getAll( elem, false ) );

				// Remove any remaining nodes
				elem.textContent = "";
			}
		}

		return this;
	},

	clone: function( dataAndEvents, deepDataAndEvents ) {
		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

		return this.map( function() {
			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
		} );
	},

	html: function( value ) {
		return access( this, function( value ) {
			var elem = this[ 0 ] || {},
				i = 0,
				l = this.length;

			if ( value === undefined && elem.nodeType === 1 ) {
				return elem.innerHTML;
			}

			// See if we can take a shortcut and just use innerHTML
			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {

				value = jQuery.htmlPrefilter( value );

				try {
					for ( ; i < l; i++ ) {
						elem = this[ i ] || {};

						// Remove element nodes and prevent memory leaks
						if ( elem.nodeType === 1 ) {
							jQuery.cleanData( getAll( elem, false ) );
							elem.innerHTML = value;
						}
					}

					elem = 0;

				// If using innerHTML throws an exception, use the fallback method
				} catch ( e ) {}
			}

			if ( elem ) {
				this.empty().append( value );
			}
		}, null, value, arguments.length );
	},

	replaceWith: function() {
		var ignored = [];

		// Make the changes, replacing each non-ignored context element with the new content
		return domManip( this, arguments, function( elem ) {
			var parent = this.parentNode;

			if ( jQuery.inArray( this, ignored ) < 0 ) {
				jQuery.cleanData( getAll( this ) );
				if ( parent ) {
					parent.replaceChild( elem, this );
				}
			}

		// Force callback invocation
		}, ignored );
	}
} );

jQuery.each( {
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function( name, original ) {
	jQuery.fn[ name ] = function( selector ) {
		var elems,
			ret = [],
			insert = jQuery( selector ),
			last = insert.length - 1,
			i = 0;

		for ( ; i <= last; i++ ) {
			elems = i === last ? this : this.clone( true );
			jQuery( insert[ i ] )[ original ]( elems );
			push.apply( ret, elems );
		}

		return this.pushStack( ret );
	};
} );

jQuery.fn.extend( {
	wrapAll: function( html ) {
		var wrap;

		if ( this[ 0 ] ) {
			if ( typeof html === "function" ) {
				html = html.call( this[ 0 ] );
			}

			// The elements to wrap the target around
			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );

			if ( this[ 0 ].parentNode ) {
				wrap.insertBefore( this[ 0 ] );
			}

			wrap.map( function() {
				var elem = this;

				while ( elem.firstElementChild ) {
					elem = elem.firstElementChild;
				}

				return elem;
			} ).append( this );
		}

		return this;
	},

	wrapInner: function( html ) {
		if ( typeof html === "function" ) {
			return this.each( function( i ) {
				jQuery( this ).wrapInner( html.call( this, i ) );
			} );
		}

		return this.each( function() {
			var self = jQuery( this ),
				contents = self.contents();

			if ( contents.length ) {
				contents.wrapAll( html );

			} else {
				self.append( html );
			}
		} );
	},

	wrap: function( html ) {
		var htmlIsFunction = typeof html === "function";

		return this.each( function( i ) {
			jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
		} );
	},

	unwrap: function( selector ) {
		this.parent( selector ).not( "body" ).each( function() {
			jQuery( this ).replaceWith( this.childNodes );
		} );
		return this;
	}
} );

var pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;

var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );

var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );

var rcustomProp = /^--/;

var cssExpand = [ "Top", "Right", "Bottom", "Left" ];

var ralphaStart = /^[a-z]/,

	// The regex visualized:
	//
	//                         /----------\
	//                        |            |    /-------\
	//                        |  / Top  \  |   |         |
	//         /--- Border ---+-| Right  |-+---+- Width -+---\
	//        |                 | Bottom |                    |
	//        |                  \ Left /                     |
	//        |                                               |
	//        |                              /----------\     |
	//        |          /-------------\    |            |    |- END
	//        |         |               |   |  / Top  \  |    |
	//        |         |  / Margin  \  |   | | Right  | |    |
	//        |---------+-|           |-+---+-| Bottom |-+----|
	//        |            \ Padding /         \ Left /       |
	// BEGIN -|                                               |
	//        |                /---------\                    |
	//        |               |           |                   |
	//        |               |  / Min \  |    / Width  \     |
	//         \--------------+-|       |-+---|          |---/
	//                           \ Max /       \ Height /
	rautoPx = /^(?:Border(?:Top|Right|Bottom|Left)?(?:Width|)|(?:Margin|Padding)?(?:Top|Right|Bottom|Left)?|(?:Min|Max)?(?:Width|Height))$/;

function isAutoPx( prop ) {

	// The first test is used to ensure that:
	// 1. The prop starts with a lowercase letter (as we uppercase it for the second regex).
	// 2. The prop is not empty.
	return ralphaStart.test( prop ) &&
		rautoPx.test( prop[ 0 ].toUpperCase() + prop.slice( 1 ) );
}

// Matches dashed string for camelizing
var rmsPrefix = /^-ms-/;

// Convert dashed to camelCase, handle vendor prefixes.
// Used by the css & effects modules.
// Support: IE <=9 - 11+
// Microsoft forgot to hump their vendor prefix (trac-9572)
function cssCamelCase( string ) {
	return camelCase( string.replace( rmsPrefix, "ms-" ) );
}

function getStyles( elem ) {

	// Support: IE <=11+ (trac-14150)
	// In IE popup's `window` is the opener window which makes `window.getComputedStyle( elem )`
	// break. Using `elem.ownerDocument.defaultView` avoids the issue.
	var view = elem.ownerDocument.defaultView;

	// `document.implementation.createHTMLDocument( "" )` has a `null` `defaultView`
	// property; check `defaultView` truthiness to fallback to window in such a case.
	if ( !view ) {
		view = window;
	}

	return view.getComputedStyle( elem );
}

// A method for quickly swapping in/out CSS properties to get correct calculations.
function swap( elem, options, callback ) {
	var ret, name,
		old = {};

	// Remember the old values, and insert the new ones
	for ( name in options ) {
		old[ name ] = elem.style[ name ];
		elem.style[ name ] = options[ name ];
	}

	ret = callback.call( elem );

	// Revert the old values
	for ( name in options ) {
		elem.style[ name ] = old[ name ];
	}

	return ret;
}

function curCSS( elem, name, computed ) {
	var ret,
		isCustomProp = rcustomProp.test( name );

	computed = computed || getStyles( elem );

	// getPropertyValue is needed for `.css('--customProperty')` (gh-3144)
	if ( computed ) {

		// A fallback to direct property access is needed as `computed`, being
		// the output of `getComputedStyle`, contains camelCased keys and
		// `getPropertyValue` requires kebab-case ones.
		//
		// Support: IE <=9 - 11+
		// IE only supports `"float"` in `getPropertyValue`; in computed styles
		// it's only available as `"cssFloat"`. We no longer modify properties
		// sent to `.css()` apart from camelCasing, so we need to check both.
		// Normally, this would create difference in behavior: if
		// `getPropertyValue` returns an empty string, the value returned
		// by `.css()` would be `undefined`. This is usually the case for
		// disconnected elements. However, in IE even disconnected elements
		// with no styles return `"none"` for `getPropertyValue( "float" )`
		ret = computed.getPropertyValue( name ) || computed[ name ];

		if ( isCustomProp && ret ) {

			// Support: Firefox 105 - 135+
			// Spec requires trimming whitespace for custom properties (gh-4926).
			// Firefox only trims leading whitespace.
			//
			// Fall back to `undefined` if empty string returned.
			// This collapses a missing definition with property defined
			// and set to an empty string but there's no standard API
			// allowing us to differentiate them without a performance penalty
			// and returning `undefined` aligns with older jQuery.
			//
			// rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED
			// as whitespace while CSS does not, but this is not a problem
			// because CSS preprocessing replaces them with U+000A LINE FEED
			// (which *is* CSS whitespace)
			// https://www.w3.org/TR/css-syntax-3/#input-preprocessing
			ret = ret.replace( rtrimCSS, "$1" ) || undefined;
		}

		if ( ret === "" && !isAttached( elem ) ) {
			ret = jQuery.style( elem, name );
		}
	}

	return ret !== undefined ?

		// Support: IE <=9 - 11+
		// IE returns zIndex value as an integer.
		ret + "" :
		ret;
}

function adjustCSS( elem, prop, valueParts, tween ) {
	var adjusted, scale,
		maxIterations = 20,
		currentValue = function() {
				return jQuery.css( elem, prop, "" );
			},
		initial = currentValue(),
		unit = valueParts && valueParts[ 3 ] || ( isAutoPx( prop ) ? "px" : "" ),

		// Starting value computation is required for potential unit mismatches
		initialInUnit = elem.nodeType &&
			( !isAutoPx( prop ) || unit !== "px" && +initial ) &&
			rcssNum.exec( jQuery.css( elem, prop ) );

	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {

		// Support: Firefox <=54 - 66+
		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
		initial = initial / 2;

		// Trust units reported by jQuery.css
		unit = unit || initialInUnit[ 3 ];

		// Iteratively approximate from a nonzero starting point
		initialInUnit = +initial || 1;

		while ( maxIterations-- ) {

			// Evaluate and update our best guess (doubling guesses that zero out).
			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
			jQuery.style( elem, prop, initialInUnit + unit );
			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
				maxIterations = 0;
			}
			initialInUnit = initialInUnit / scale;

		}

		initialInUnit = initialInUnit * 2;
		jQuery.style( elem, prop, initialInUnit + unit );

		// Make sure we update the tween properties later on
		valueParts = valueParts || [];
	}

	if ( valueParts ) {
		initialInUnit = +initialInUnit || +initial || 0;

		// Apply relative offset (+=/-=) if specified
		adjusted = valueParts[ 1 ] ?
			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
			+valueParts[ 2 ];
	}
	return adjusted;
}

var cssPrefixes = [ "Webkit", "Moz", "ms" ],
	emptyStyle = document$1.createElement( "div" ).style;

// Return a vendor-prefixed property or undefined
function vendorPropName( name ) {

	// Check for vendor prefixed names
	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
		i = cssPrefixes.length;

	while ( i-- ) {
		name = cssPrefixes[ i ] + capName;
		if ( name in emptyStyle ) {
			return name;
		}
	}
}

// Return a potentially-mapped vendor prefixed property
function finalPropName( name ) {
	if ( name in emptyStyle ) {
		return name;
	}
	return vendorPropName( name ) || name;
}

var reliableTrDimensionsVal, reliableColDimensionsVal,
	table = document$1.createElement( "table" );

// Executing table tests requires only one layout, so they're executed
// at the same time to save the second computation.
function computeTableStyleTests() {
	if (

		// This is a singleton, we need to execute it only once
		!table ||

		// Finish early in limited (non-browser) environments
		!table.style
	) {
		return;
	}

	var trStyle,
		col = document$1.createElement( "col" ),
		tr = document$1.createElement( "tr" ),
		td = document$1.createElement( "td" );

	table.style.cssText = "position:absolute;left:-11111px;" +
		"border-collapse:separate;border-spacing:0";
	tr.style.cssText = "box-sizing:content-box;border:1px solid;height:1px";
	td.style.cssText = "height:9px;width:9px;padding:0";

	col.span = 2;

	documentElement$1
		.appendChild( table )
		.appendChild( col )
		.parentNode
		.appendChild( tr )
		.appendChild( td )
		.parentNode
		.appendChild( td.cloneNode( true ) );

	// Don't run until window is visible
	if ( table.offsetWidth === 0 ) {
		documentElement$1.removeChild( table );
		return;
	}

	trStyle = window.getComputedStyle( tr );

	// Support: Firefox 135+
	// Firefox always reports computed width as if `span` was 1.
	// Support: Safari 18.3+
	// In Safari, computed width for columns is always 0.
	// In both these browsers, using `offsetWidth` solves the issue.
	// Support: IE 11+
	// In IE, `<col>` computed width is `"auto"` unless `width` is set
	// explicitly via CSS so measurements there remain incorrect. Because of
	// the lack of a proper workaround, we accept this limitation, treating
	// IE as passing the test.
	reliableColDimensionsVal = isIE || Math.round( parseFloat(
		window.getComputedStyle( col ).width )
	) === 18;

	// Support: IE 10 - 11+
	// IE misreports `getComputedStyle` of table rows with width/height
	// set in CSS while `offset*` properties report correct values.
	// Support: Firefox 70 - 135+
	// Only Firefox includes border widths
	// in computed dimensions for table rows. (gh-4529)
	reliableTrDimensionsVal = Math.round( parseFloat( trStyle.height ) +
		parseFloat( trStyle.borderTopWidth ) +
		parseFloat( trStyle.borderBottomWidth ) ) === tr.offsetHeight;

	documentElement$1.removeChild( table );

	// Nullify the table so it wouldn't be stored in the memory;
	// it will also be a sign that checks were already performed.
	table = null;
}

jQuery.extend( support, {
	reliableTrDimensions: function() {
		computeTableStyleTests();
		return reliableTrDimensionsVal;
	},

	reliableColDimensions: function() {
		computeTableStyleTests();
		return reliableColDimensionsVal;
	}
} );

var cssShow = { position: "absolute", visibility: "hidden", display: "block" },
	cssNormalTransform = {
		letterSpacing: "0",
		fontWeight: "400"
	};

function setPositiveNumber( _elem, value, subtract ) {

	// Any relative (+/-) values have already been
	// normalized at this point
	var matches = rcssNum.exec( value );
	return matches ?

		// Guard against undefined "subtract", e.g., when used as in cssHooks
		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
		value;
}

function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
	var i = dimension === "width" ? 1 : 0,
		extra = 0,
		delta = 0,
		marginDelta = 0;

	// Adjustment may not be necessary
	if ( box === ( isBorderBox ? "border" : "content" ) ) {
		return 0;
	}

	for ( ; i < 4; i += 2 ) {

		// Both box models exclude margin
		// Count margin delta separately to only add it after scroll gutter adjustment.
		// This is needed to make negative margins work with `outerHeight( true )` (gh-3982).
		if ( box === "margin" ) {
			marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
		}

		// If we get here with a content-box, we're seeking "padding" or "border" or "margin"
		if ( !isBorderBox ) {

			// Add padding
			delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );

			// For "border" or "margin", add border
			if ( box !== "padding" ) {
				delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );

			// But still keep track of it otherwise
			} else {
				extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}

		// If we get here with a border-box (content + padding + border), we're seeking "content" or
		// "padding" or "margin"
		} else {

			// For "content", subtract padding
			if ( box === "content" ) {
				delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
			}

			// For "content" or "padding", subtract border
			if ( box !== "margin" ) {
				delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}
		}
	}

	// Account for positive content-box scroll gutter when requested by providing computedVal
	if ( !isBorderBox && computedVal >= 0 ) {

		// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
		// Assuming integer scroll gutter, subtract the rest and round down
		delta += Math.max( 0, Math.ceil(
			elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
			computedVal -
			delta -
			extra -
			0.5

		// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
		// Use an explicit zero to avoid NaN (gh-3964)
		) ) || 0;
	}

	return delta + marginDelta;
}

function getWidthOrHeight( elem, dimension, extra ) {

	// Start with computed style
	var styles = getStyles( elem ),

		// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
		// Fake content-box until we know it's needed to know the true value.
		boxSizingNeeded = isIE || extra,
		isBorderBox = boxSizingNeeded &&
			jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
		valueIsBorderBox = isBorderBox,

		val = curCSS( elem, dimension, styles ),
		offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );

	// Return a confounding non-pixel value or feign ignorance, as appropriate.
	if ( rnumnonpx.test( val ) ) {
		if ( !extra ) {
			return val;
		}
		val = "auto";
	}


	if (
		(

			// Fall back to offsetWidth/offsetHeight when value is "auto"
			// This happens for inline elements with no explicit setting (gh-3571)
			val === "auto" ||

			// Support: IE 9 - 11+
			// Use offsetWidth/offsetHeight for when box sizing is unreliable.
			// In those cases, the computed value can be trusted to be border-box.
			( isIE && isBorderBox ) ||

			( !support.reliableColDimensions() && nodeName( elem, "col" ) ) ||

			( !support.reliableTrDimensions() && nodeName( elem, "tr" ) )
		) &&

		// Make sure the element is visible & connected
		elem.getClientRects().length ) {

		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";

		// Where available, offsetWidth/offsetHeight approximate border box dimensions.
		// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
		// retrieved value as a content box dimension.
		valueIsBorderBox = offsetProp in elem;
		if ( valueIsBorderBox ) {
			val = elem[ offsetProp ];
		}
	}

	// Normalize "" and auto
	val = parseFloat( val ) || 0;

	// Adjust for the element's box model
	return ( val +
		boxModelAdjustment(
			elem,
			dimension,
			extra || ( isBorderBox ? "border" : "content" ),
			valueIsBorderBox,
			styles,

			// Provide the current computed size to request scroll gutter calculation (gh-3589)
			val
		)
	) + "px";
}

jQuery.extend( {

	// Add in style property hooks for overriding the default
	// behavior of getting and setting a style property
	cssHooks: {},

	// Get and set the style property on a DOM Node
	style: function( elem, name, value, extra ) {

		// Don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
			return;
		}

		// Make sure that we're working with the right name
		var ret, type, hooks,
			origName = cssCamelCase( name ),
			isCustomProp = rcustomProp.test( name ),
			style = elem.style;

		// Make sure that we're working with the right name. We don't
		// want to query the value if it is a CSS custom property
		// since they are user-defined.
		if ( !isCustomProp ) {
			name = finalPropName( origName );
		}

		// Gets hook for the prefixed version, then unprefixed version
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// Check if we're setting a value
		if ( value !== undefined ) {
			type = typeof value;

			// Convert "+=" or "-=" to relative numbers (trac-7345)
			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
				value = adjustCSS( elem, name, ret );

				// Fixes bug trac-9237
				type = "number";
			}

			// Make sure that null and NaN values aren't set (trac-7116)
			if ( value == null || value !== value ) {
				return;
			}

			// If the value is a number, add `px` for certain CSS properties
			if ( type === "number" ) {
				value += ret && ret[ 3 ] || ( isAutoPx( origName ) ? "px" : "" );
			}

			// Support: IE <=9 - 11+
			// background-* props of a cloned element affect the source element (trac-8908)
			if ( isIE && value === "" && name.indexOf( "background" ) === 0 ) {
				style[ name ] = "inherit";
			}

			// If a hook was provided, use that value, otherwise just set the specified value
			if ( !hooks || !( "set" in hooks ) ||
				( value = hooks.set( elem, value, extra ) ) !== undefined ) {

				if ( isCustomProp ) {
					style.setProperty( name, value );
				} else {
					style[ name ] = value;
				}
			}

		} else {

			// If a hook was provided get the non-computed value from there
			if ( hooks && "get" in hooks &&
				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {

				return ret;
			}

			// Otherwise just get the value from the style object
			return style[ name ];
		}
	},

	css: function( elem, name, extra, styles ) {
		var val, num, hooks,
			origName = cssCamelCase( name ),
			isCustomProp = rcustomProp.test( name );

		// Make sure that we're working with the right name. We don't
		// want to modify the value if it is a CSS custom property
		// since they are user-defined.
		if ( !isCustomProp ) {
			name = finalPropName( origName );
		}

		// Try prefixed name followed by the unprefixed name
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// If a hook was provided get the computed value from there
		if ( hooks && "get" in hooks ) {
			val = hooks.get( elem, true, extra );
		}

		// Otherwise, if a way to get the computed value exists, use that
		if ( val === undefined ) {
			val = curCSS( elem, name, styles );
		}

		// Convert "normal" to computed value
		if ( val === "normal" && name in cssNormalTransform ) {
			val = cssNormalTransform[ name ];
		}

		// Make numeric if forced or a qualifier was provided and val looks numeric
		if ( extra === "" || extra ) {
			num = parseFloat( val );
			return extra === true || isFinite( num ) ? num || 0 : val;
		}

		return val;
	}
} );

jQuery.each( [ "height", "width" ], function( _i, dimension ) {
	jQuery.cssHooks[ dimension ] = {
		get: function( elem, computed, extra ) {
			if ( computed ) {

				// Elements with `display: none` can have dimension info if
				// we invisibly show them.
				return jQuery.css( elem, "display" ) === "none" ?
					swap( elem, cssShow, function() {
						return getWidthOrHeight( elem, dimension, extra );
					} ) :
					getWidthOrHeight( elem, dimension, extra );
			}
		},

		set: function( elem, value, extra ) {
			var matches,
				styles = getStyles( elem ),

				// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
				isBorderBox = extra &&
					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
				subtract = extra ?
					boxModelAdjustment(
						elem,
						dimension,
						extra,
						isBorderBox,
						styles
					) :
					0;

			// Convert to pixels if value adjustment is needed
			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
				( matches[ 3 ] || "px" ) !== "px" ) {

				elem.style[ dimension ] = value;
				value = jQuery.css( elem, dimension );
			}

			return setPositiveNumber( elem, value, subtract );
		}
	};
} );

// These hooks are used by animate to expand properties
jQuery.each( {
	margin: "",
	padding: "",
	border: "Width"
}, function( prefix, suffix ) {
	jQuery.cssHooks[ prefix + suffix ] = {
		expand: function( value ) {
			var i = 0,
				expanded = {},

				// Assumes a single number if not a string
				parts = typeof value === "string" ? value.split( " " ) : [ value ];

			for ( ; i < 4; i++ ) {
				expanded[ prefix + cssExpand[ i ] + suffix ] =
					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
			}

			return expanded;
		}
	};

	if ( prefix !== "margin" ) {
		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
	}
} );

jQuery.fn.extend( {
	css: function( name, value ) {
		return access( this, function( elem, name, value ) {
			var styles, len,
				map = {},
				i = 0;

			if ( Array.isArray( name ) ) {
				styles = getStyles( elem );
				len = name.length;

				for ( ; i < len; i++ ) {
					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
				}

				return map;
			}

			return value !== undefined ?
				jQuery.style( elem, name, value ) :
				jQuery.css( elem, name );
		}, name, value, arguments.length > 1 );
	}
} );

jQuery.expr.pseudos.hidden = function( elem ) {
	return !jQuery.expr.pseudos.visible( elem );
};
jQuery.expr.pseudos.visible = function( elem ) {
	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

// isHiddenWithinTree reports if an element has a non-"none" display style (inline and/or
// through the CSS cascade), which is useful in deciding whether or not to make it visible.
// It differs from the :hidden selector (jQuery.expr.pseudos.hidden) in two important ways:
// * A hidden ancestor does not force an element to be classified as hidden.
// * Being disconnected from the document does not force an element to be classified as hidden.
// These differences improve the behavior of .toggle() et al. when applied to elements that are
// detached or contained within hidden ancestors (gh-2404, gh-2863).
function isHiddenWithinTree( elem, el ) {

	// isHiddenWithinTree might be called from jQuery#filter function;
	// in that case, element will be second argument
	elem = elem;

	// Inline style trumps all
	return elem.style.display === "none" ||
		elem.style.display === "" &&
		jQuery.css( elem, "display" ) === "none";
}

var defaultDisplayMap = {};

function getDefaultDisplay( elem ) {
	var temp,
		doc = elem.ownerDocument,
		nodeName = elem.nodeName,
		display = defaultDisplayMap[ nodeName ];

	if ( display ) {
		return display;
	}

	temp = doc.body.appendChild( doc.createElement( nodeName ) );
	display = jQuery.css( temp, "display" );

	temp.parentNode.removeChild( temp );

	if ( display === "none" ) {
		display = "block";
	}
	defaultDisplayMap[ nodeName ] = display;

	return display;
}

function showHide( elements, show ) {
	var display, elem,
		values = [],
		index = 0,
		length = elements.length;

	// Determine new display value for elements that need to change
	for ( ; index < length; index++ ) {
		elem = elements[ index ];
		if ( !elem.style ) {
			continue;
		}

		display = elem.style.display;
		if ( show ) {

			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
			// check is required in this first loop unless we have a nonempty display value (either
			// inline or about-to-be-restored)
			if ( display === "none" ) {
				values[ index ] = dataPriv.get( elem, "display" ) || null;
				if ( !values[ index ] ) {
					elem.style.display = "";
				}
			}
			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
				values[ index ] = getDefaultDisplay( elem );
			}
		} else {
			if ( display !== "none" ) {
				values[ index ] = "none";

				// Remember what we're overwriting
				dataPriv.set( elem, "display", display );
			}
		}
	}

	// Set the display of the elements in a second loop to avoid constant reflow
	for ( index = 0; index < length; index++ ) {
		if ( values[ index ] != null ) {
			elements[ index ].style.display = values[ index ];
		}
	}

	return elements;
}

jQuery.fn.extend( {
	show: function() {
		return showHide( this, true );
	},
	hide: function() {
		return showHide( this );
	},
	toggle: function( state ) {
		if ( typeof state === "boolean" ) {
			return state ? this.show() : this.hide();
		}

		return this.each( function() {
			if ( isHiddenWithinTree( this ) ) {
				jQuery( this ).show();
			} else {
				jQuery( this ).hide();
			}
		} );
	}
} );

var
	rbracket = /\[\]$/,
	rCRLF = /\r?\n/g,
	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
	rsubmittable = /^(?:input|select|textarea|keygen)/i;

function buildParams( prefix, obj, traditional, add ) {
	var name;

	if ( Array.isArray( obj ) ) {

		// Serialize array item.
		jQuery.each( obj, function( i, v ) {
			if ( traditional || rbracket.test( prefix ) ) {

				// Treat each array item as a scalar.
				add( prefix, v );

			} else {

				// Item is non-scalar (array or object), encode its numeric index.
				buildParams(
					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
					v,
					traditional,
					add
				);
			}
		} );

	} else if ( !traditional && toType( obj ) === "object" ) {

		// Serialize object item.
		for ( name in obj ) {
			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
		}

	} else {

		// Serialize scalar item.
		add( prefix, obj );
	}
}

// Serialize an array of form elements or a set of
// key/values into a query string
jQuery.param = function( a, traditional ) {
	var prefix,
		s = [],
		add = function( key, valueOrFunction ) {

			// If value is a function, invoke it and use its return value
			var value = typeof valueOrFunction === "function" ?
				valueOrFunction() :
				valueOrFunction;

			s[ s.length ] = encodeURIComponent( key ) + "=" +
				encodeURIComponent( value == null ? "" : value );
		};

	if ( a == null ) {
		return "";
	}

	// If an array was passed in, assume that it is an array of form elements.
	if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {

		// Serialize the form elements
		jQuery.each( a, function() {
			add( this.name, this.value );
		} );

	} else {

		// If traditional, encode the "old" way (the way 1.3.2 or older
		// did it), otherwise encode params recursively.
		for ( prefix in a ) {
			buildParams( prefix, a[ prefix ], traditional, add );
		}
	}

	// Return the resulting serialization
	return s.join( "&" );
};

jQuery.fn.extend( {
	serialize: function() {
		return jQuery.param( this.serializeArray() );
	},
	serializeArray: function() {
		return this.map( function() {

			// Can add propHook for "elements" to filter or add form elements
			var elements = jQuery.prop( this, "elements" );
			return elements ? jQuery.makeArray( elements ) : this;
		} ).filter( function() {
			var type = this.type;

			// Use .is( ":disabled" ) so that fieldset[disabled] works
			return this.name && !jQuery( this ).is( ":disabled" ) &&
				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
				( this.checked || !rcheckableType.test( type ) );
		} ).map( function( _i, elem ) {
			var val = jQuery( this ).val();

			if ( val == null ) {
				return null;
			}

			if ( Array.isArray( val ) ) {
				return jQuery.map( val, function( val ) {
					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
				} );
			}

			return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
		} ).get();
	}
} );

// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
	var xml, parserErrorElem;
	if ( !data || typeof data !== "string" ) {
		return null;
	}

	// Support: IE 9 - 11+
	// IE throws on parseFromString with invalid input.
	try {
		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
	} catch ( e ) {}

	parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ];
	if ( !xml || parserErrorElem ) {
		jQuery.error( "Invalid XML: " + (
			parserErrorElem ?
				jQuery.map( parserErrorElem.childNodes, function( el ) {
					return el.textContent;
				} ).join( "\n" ) :
				data
		) );
	}
	return xml;
};

// Argument "data" should be string of html or a TrustedHTML wrapper of obvious HTML
// context (optional): If specified, the fragment will be created in this context,
// defaults to document
// keepScripts (optional): If true, will include scripts passed in the html string
jQuery.parseHTML = function( data, context, keepScripts ) {
	if ( typeof data !== "string" && !isObviousHtml( data + "" ) ) {
		return [];
	}
	if ( typeof context === "boolean" ) {
		keepScripts = context;
		context = false;
	}

	var parsed, scripts;

	if ( !context ) {

		// Stop scripts or inline event handlers from being executed immediately
		// by using DOMParser
		context = ( new window.DOMParser() )
			.parseFromString( "", "text/html" );
	}

	parsed = rsingleTag.exec( data );
	scripts = !keepScripts && [];

	// Single tag
	if ( parsed ) {
		return [ context.createElement( parsed[ 1 ] ) ];
	}

	parsed = buildFragment( [ data ], context, scripts );

	if ( scripts && scripts.length ) {
		jQuery( scripts ).remove();
	}

	return jQuery.merge( [], parsed.childNodes );
};

jQuery.offset = {
	setOffset: function( elem, options, i ) {
		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
			position = jQuery.css( elem, "position" ),
			curElem = jQuery( elem ),
			props = {};

		// Set position first, in-case top/left are set even on static elem
		if ( position === "static" ) {
			elem.style.position = "relative";
		}

		curOffset = curElem.offset();
		curCSSTop = jQuery.css( elem, "top" );
		curCSSLeft = jQuery.css( elem, "left" );
		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;

		// Need to be able to calculate position if either
		// top or left is auto and position is either absolute or fixed
		if ( calculatePosition ) {
			curPosition = curElem.position();
			curTop = curPosition.top;
			curLeft = curPosition.left;

		} else {
			curTop = parseFloat( curCSSTop ) || 0;
			curLeft = parseFloat( curCSSLeft ) || 0;
		}

		if ( typeof options === "function" ) {

			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
		}

		if ( options.top != null ) {
			props.top = ( options.top - curOffset.top ) + curTop;
		}
		if ( options.left != null ) {
			props.left = ( options.left - curOffset.left ) + curLeft;
		}

		if ( "using" in options ) {
			options.using.call( elem, props );

		} else {
			curElem.css( props );
		}
	}
};

jQuery.fn.extend( {

	// offset() relates an element's border box to the document origin
	offset: function( options ) {

		// Preserve chaining for setter
		if ( arguments.length ) {
			return options === undefined ?
				this :
				this.each( function( i ) {
					jQuery.offset.setOffset( this, options, i );
				} );
		}

		var rect, win,
			elem = this[ 0 ];

		if ( !elem ) {
			return;
		}

		// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
		// Support: IE <=11+
		// Running getBoundingClientRect on a
		// disconnected node in IE throws an error
		if ( !elem.getClientRects().length ) {
			return { top: 0, left: 0 };
		}

		// Get document-relative position by adding viewport scroll to viewport-relative gBCR
		rect = elem.getBoundingClientRect();
		win = elem.ownerDocument.defaultView;
		return {
			top: rect.top + win.pageYOffset,
			left: rect.left + win.pageXOffset
		};
	},

	// position() relates an element's margin box to its offset parent's padding box
	// This corresponds to the behavior of CSS absolute positioning
	position: function() {
		if ( !this[ 0 ] ) {
			return;
		}

		var offsetParent, offset, doc,
			elem = this[ 0 ],
			parentOffset = { top: 0, left: 0 };

		// position:fixed elements are offset from the viewport, which itself always has zero offset
		if ( jQuery.css( elem, "position" ) === "fixed" ) {

			// Assume position:fixed implies availability of getBoundingClientRect
			offset = elem.getBoundingClientRect();

		} else {
			offset = this.offset();

			// Account for the *real* offset parent, which can be the document or its root element
			// when a statically positioned element is identified
			doc = elem.ownerDocument;
			offsetParent = elem.offsetParent || doc.documentElement;
			while ( offsetParent &&
				offsetParent !== doc.documentElement &&
				jQuery.css( offsetParent, "position" ) === "static" ) {

				offsetParent = offsetParent.offsetParent || doc.documentElement;
			}
			if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 &&
				jQuery.css( offsetParent, "position" ) !== "static" ) {

				// Incorporate borders into its offset, since they are outside its content origin
				parentOffset = jQuery( offsetParent ).offset();
				parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
				parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
			}
		}

		// Subtract parent offsets and element margins
		return {
			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
		};
	},

	// This method will return documentElement in the following cases:
	// 1) For the element inside the iframe without offsetParent, this method will return
	//    documentElement of the parent window
	// 2) For the hidden or detached element
	// 3) For body or html element, i.e. in case of the html node - it will return itself
	//
	// but those exceptions were never presented as a real life use-cases
	// and might be considered as more preferable results.
	//
	// This logic, however, is not guaranteed and can change at any point in the future
	offsetParent: function() {
		return this.map( function() {
			var offsetParent = this.offsetParent;

			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
				offsetParent = offsetParent.offsetParent;
			}

			return offsetParent || documentElement$1;
		} );
	}
} );

// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
	var top = "pageYOffset" === prop;

	jQuery.fn[ method ] = function( val ) {
		return access( this, function( elem, method, val ) {

			// Coalesce documents and windows
			var win;
			if ( isWindow( elem ) ) {
				win = elem;
			} else if ( elem.nodeType === 9 ) {
				win = elem.defaultView;
			}

			if ( val === undefined ) {
				return win ? win[ prop ] : elem[ method ];
			}

			if ( win ) {
				win.scrollTo(
					!top ? val : win.pageXOffset,
					top ? val : win.pageYOffset
				);

			} else {
				elem[ method ] = val;
			}
		}, method, val, arguments.length );
	};
} );

// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
	jQuery.each( {
		padding: "inner" + name,
		content: type,
		"": "outer" + name
	}, function( defaultExtra, funcName ) {

		// Margin is only for outerHeight, outerWidth
		jQuery.fn[ funcName ] = function( margin, value ) {
			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );

			return access( this, function( elem, type, value ) {
				var doc;

				if ( isWindow( elem ) ) {

					// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
					return funcName.indexOf( "outer" ) === 0 ?
						elem[ "inner" + name ] :
						elem.document.documentElement[ "client" + name ];
				}

				// Get document width or height
				if ( elem.nodeType === 9 ) {
					doc = elem.documentElement;

					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
					// whichever is greatest
					return Math.max(
						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
						elem.body[ "offset" + name ], doc[ "offset" + name ],
						doc[ "client" + name ]
					);
				}

				return value === undefined ?

					// Get width or height on the element, requesting but not forcing parseFloat
					jQuery.css( elem, type, extra ) :

					// Set width or height on the element
					jQuery.style( elem, type, value, extra );
			}, type, chainable ? margin : undefined, chainable );
		};
	} );
} );

jQuery.fn.extend( {

	bind: function( types, data, fn ) {
		return this.on( types, null, data, fn );
	},
	unbind: function( types, fn ) {
		return this.off( types, null, fn );
	},

	delegate: function( selector, types, data, fn ) {
		return this.on( types, selector, data, fn );
	},
	undelegate: function( selector, types, fn ) {

		// ( namespace ) or ( selector, types [, fn] )
		return arguments.length === 1 ?
			this.off( selector, "**" ) :
			this.off( types, selector || "**", fn );
	},

	hover: function( fnOver, fnOut ) {
		return this
			.on( "mouseenter", fnOver )
			.on( "mouseleave", fnOut || fnOver );
	}
} );

jQuery.each(
	( "blur focus focusin focusout resize scroll click dblclick " +
	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
	function( _i, name ) {

		// Handle event binding
		jQuery.fn[ name ] = function( data, fn ) {
			return arguments.length > 0 ?
				this.on( name, null, data, fn ) :
				this.trigger( name );
		};
	}
);

// Bind a function to a context, optionally partially applying any
// arguments.
// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
// However, it is not slated for removal any time soon
jQuery.proxy = function( fn, context ) {
	var tmp, args, proxy;

	if ( typeof context === "string" ) {
		tmp = fn[ context ];
		context = fn;
		fn = tmp;
	}

	// Quick check to determine if target is callable, in the spec
	// this throws a TypeError, but we will just return undefined.
	if ( typeof fn !== "function" ) {
		return undefined;
	}

	// Simulated bind
	args = slice.call( arguments, 2 );
	proxy = function() {
		return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
	};

	// Set the guid of unique handler to the same of original handler, so it can be removed
	proxy.guid = fn.guid = fn.guid || jQuery.guid++;

	return proxy;
};

jQuery.holdReady = function( hold ) {
	if ( hold ) {
		jQuery.readyWait++;
	} else {
		jQuery.ready( true );
	}
};

jQuery.expr[ ":" ] = jQuery.expr.filters = jQuery.expr.pseudos;

// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

if ( typeof define === "function" && define.amd ) {
	define( "jquery", [], function() {
		return jQuery;
	} );
}

var

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

jQuery.noConflict = function( deep ) {
	if ( window.$ === jQuery ) {
		window.$ = _$;
	}

	if ( deep && window.jQuery === jQuery ) {
		window.jQuery = _jQuery;
	}

	return jQuery;
};

// Expose jQuery and $ identifiers, even in AMD
// (trac-7102#comment:10, gh-557)
// and CommonJS for browser emulators (trac-13566)
if ( typeof noGlobal === "undefined" ) {
	window.jQuery = window.$ = jQuery;
}

var readyCallbacks = [],
	whenReady = function( fn ) {
		readyCallbacks.push( fn );
	},
	executeReady = function( fn ) {

		// Prevent errors from freezing future callback execution (gh-1823)
		// Not backwards-compatible as this does not execute sync
		window.setTimeout( function() {
			fn.call( document$1, jQuery );
		} );
	};

jQuery.fn.ready = function( fn ) {
	whenReady( fn );
	return this;
};

jQuery.extend( {

	// Is the DOM ready to be used? Set to true once it occurs.
	isReady: false,

	// A counter to track how many items to wait for before
	// the ready event fires. See trac-6781
	readyWait: 1,

	ready: function( wait ) {

		// Abort if there are pending holds or we're already ready
		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
			return;
		}

		// Remember that the DOM is ready
		jQuery.isReady = true;

		// If a normal DOM Ready event fired, decrement, and wait if need be
		if ( wait !== true && --jQuery.readyWait > 0 ) {
			return;
		}

		whenReady = function( fn ) {
			readyCallbacks.push( fn );

			while ( readyCallbacks.length ) {
				fn = readyCallbacks.shift();
				if ( typeof fn === "function" ) {
					executeReady( fn );
				}
			}
		};

		whenReady();
	}
} );

// Make jQuery.ready Promise consumable (gh-1778)
jQuery.ready.then = jQuery.fn.ready;

/**
 * The ready event handler and self cleanup method
 */
function completed() {
	document$1.removeEventListener( "DOMContentLoaded", completed );
	window.removeEventListener( "load", completed );
	jQuery.ready();
}

// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
if ( document$1.readyState !== "loading" ) {

	// Handle it asynchronously to allow scripts the opportunity to delay ready
	window.setTimeout( jQuery.ready );

} else {

	// Use the handy event callback
	document$1.addEventListener( "DOMContentLoaded", completed );

	// A fallback to window.onload, that will always work
	window.addEventListener( "load", completed );
}

return jQuery;

}

var jQuery = jQueryFactory( window, true );



const __default__ = jQuery;
;

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_src_x_ite_Namespace_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A .add ("jquery.slim.module", __default__));
/* harmony export */ __webpack_require__.d(__webpack_exports__, [
/* harmony export */   "A", 0, /* export default binding */ __WEBPACK_DEFAULT_EXPORT__
/* harmony export */ ]);


/***/ }

/******/ });
/************************************************************************/
/******/ // The module cache
/******/ const __webpack_module_cache__ = {};
/******/ 
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ 	// Check if module is in cache
/******/ 	const cachedModule = __webpack_module_cache__[moduleId];
/******/ 	if (cachedModule !== undefined) {
/******/ 		return cachedModule.exports;
/******/ 	}
/******/ 	// Create a new module (and put it into the cache)
/******/ 	const module = __webpack_module_cache__[moduleId] = {
/******/ 		// no module.id needed
/******/ 		// no module.loaded needed
/******/ 		exports: {}
/******/ 	};
/******/ 
/******/ 	// Execute the module function
/******/ 	__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ 
/******/ 	// Return the exports of the module
/******/ 	return module.exports;
/******/ }
/******/ 
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ 	// define getter/value functions for harmony exports
/******/ 	__webpack_require__.d = (exports, definition) => {
/******/ 		if(Array.isArray(definition)) {
/******/ 			var i = 0;
/******/ 			while(i < definition.length) {
/******/ 				var key = definition[i++];
/******/ 				var binding = definition[i++];
/******/ 				if(!__webpack_require__.o(exports, key)) {
/******/ 					if(binding === 0) {
/******/ 						Object.defineProperty(exports, key, { enumerable: true, value: definition[i++] });
/******/ 					} else {
/******/ 						Object.defineProperty(exports, key, { enumerable: true, get: binding });
/******/ 					}
/******/ 				} else if(binding === 0) { i++; }
/******/ 			}
/******/ 		} else {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		}
/******/ 	};
/******/ })();
/******/ 
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ 	__webpack_require__.g = (function() {
/******/ 		if (typeof globalThis === 'object') return globalThis;
/******/ 		try {
/******/ 			return this || new Function('return this')();
/******/ 		} catch (e) {
/******/ 			if (typeof window === 'object') return window;
/******/ 		}
/******/ 	})();
/******/ })();
/******/ 
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ 	__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/ 
/************************************************************************/

// EXTERNAL MODULE: ./src/x_ite/Namespace.js
var Namespace = __webpack_require__(226);
;// ./src/x_ite/Base/X3DConstants.js
const X3DConstants =
{
   [Symbol .toStringTag]: "X3DConstants",
};

let CONSTANT_VALUE = 1000;

Object .defineProperty (X3DConstants, "addConstant",
{
   value (name, value)
   {
      if (this .hasOwnProperty (name) || this .hasOwnProperty (value))
         return;

      Object .defineProperty (this, name,
      {
         value: value ?? ++ CONSTANT_VALUE,
         enumerable: true,
      });

      Object .defineProperty (this, this [name],
      {
         value: name,
      });
   },
});

Object .entries ({
   // Access types

   initializeOnly: 0b001,
   inputOnly:      0b010,
   outputOnly:     0b100,
   inputOutput:    0b111,
})
.forEach (([name, value]) => X3DConstants .addConstant (name, value));

[
   // Browser events

   "CONNECTION_ERROR",
   "BROWSER_EVENT",
   "INITIALIZED_EVENT",
   "SHUTDOWN_EVENT",
   "INITIALIZED_ERROR",

   // Load states

   "NOT_STARTED_STATE",
   "IN_PROGRESS_STATE",
   "COMPLETE_STATE",
   "FAILED_STATE",

   // X3DField

   "SFBool",
   "SFColor",
   "SFColorRGBA",
   "SFDouble",
   "SFFloat",
   "SFImage",
   "SFInt32",
   "SFMatrix3d",
   "SFMatrix3f",
   "SFMatrix4d",
   "SFMatrix4f",
   "SFNode",
   "SFQuaternion",
   "SFRotation",
   "SFString",
   "SFTime",
   "SFVec2d",
   "SFVec2f",
   "SFVec3d",
   "SFVec3f",
   "SFVec4d",
   "SFVec4f",

   "VrmlMatrix",

   // X3DArrayField

   "MFBool",
   "MFColor",
   "MFColorRGBA",
   "MFDouble",
   "MFFloat",
   "MFImage",
   "MFInt32",
   "MFMatrix3d",
   "MFMatrix3f",
   "MFMatrix4d",
   "MFMatrix4f",
   "MFNode",
   "MFQuaternion",
   "MFRotation",
   "MFString",
   "MFTime",
   "MFVec2d",
   "MFVec2f",
   "MFVec3d",
   "MFVec3f",
   "MFVec4d",
   "MFVec4f",

   // Abstract and concrete nodes and nodes types are added later.
]
.forEach (name => X3DConstants .addConstant (name));

const __default__ = X3DConstants;
;

/* harmony default export */ const Base_X3DConstants = (Namespace/* default */.A .add ("X3DConstants", __default__));
;// ./src/standard/Math/Algorithm.js
const Algorithm =
{
   radians (value)
   {
      // Convert degrees to radians.
      return value * (Math .PI / 180);
   },
   degrees (value)
   {
      // Convert radians to degrees.
      return value * (180 / Math .PI);
   },
   random (min, max)
   {
      return min + Math .random () * (max - min);
   },
   fract (value)
   {
      return value % 1;
   },
   clamp (value, min, max)
   {
      // https://registry.khronos.org/OpenGL-Refpages/gl4/html/clamp.xhtml
      return Math .min (Math .max (value, min), max);
   },
   interval (value, low, high)
   {
      if (value >= high)
         return ((value - low) % (high - low)) + low;

      if (value < low)
         return ((value - high) % (high - low)) + high;

      return value;
   },
   project (value, fromLow, fromHigh, toLow, toHigh)
   {
      return toLow + ((value - fromLow) / (fromHigh - fromLow)) * (toHigh - toLow);
   },
   roundToMultiple (value, multiple)
   {
      return Math .ceil (value / multiple) * multiple;
   },
   lerp (source, destination, t)
   {
      return source + t * (destination - source);
   },
   slerp (source, destination, t, short = true)
   {
      let cosom = source .dot (destination);

      // if (cosom <= -1) ... vectors are inverse colinear.
      // ; // This case is not handled.

      if (short && cosom < 0)
      {
         // Reverse signs so we travel the short way round.
         cosom = -cosom;
         destination .negate ();
      }

      if (cosom >= 1) // Both normal vectors are equal.
         return source;

      const
         omega  = Math .acos (cosom),
         sinom  = Math .sin  (omega),
         scale0 = Math .sin ((1 - t) * omega) / sinom,
         scale1 = Math .sin (t * omega) / sinom;

      source .x = source .x * scale0 + destination .x * scale1;
      source .y = source .y * scale0 + destination .y * scale1;
      source .z = source .z * scale0 + destination .z * scale1;
      source .w = source .w * scale0 + destination .w * scale1;

      return source;
   },
   simpleSlerp (source, destination, t)
   {
      return this .slerp (source, destination, t, false);
   },
   isPowerOfTwo (n)
   {
      return ((n - 1) & n) === 0;
   },
   nextPowerOfTwo (n)
   {
      ///  Returns the next power of two of @a n. If n is a power of two, n is returned.

      if (Algorithm .isPowerOfTwo (n))
         return n;

      return 1 << (32 - Math .clz32 (n));
   },
   bitCount (n)
   {
      n = n - ((n >>> 1) & 0x55555555);
      n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
      return ((n + (n >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
   },
   cmp (lhs, rhs)
   {
      return lhs > rhs ? 1 : lhs < rhs ? -1 : 0;
   },
   less (lhs, rhs)
   {
      return lhs < rhs;
   },
   greater (lhs, rhs)
   {
      return lhs > rhs;
   },
   lowerBound (array, first, last, value, comp = Algorithm.less)
   {
      // https://en.cppreference.com/w/cpp/algorithm/lower_bound

      let
         index = 0,
         step  = 0,
         count = last - first;

      while (count > 0)
      {
         step  = count >>> 1;
         index = first + step;

         if (comp (array [index], value))
         {
            first  = ++ index;
            count -= step + 1;
         }
         else
         {
            count = step;
         }
      }

      return first;
   },
   upperBound (array, first, last, value, comp = Algorithm.less)
   {
      // https://en.cppreference.com/w/cpp/algorithm/upper_bound

      let
         index = 0,
         step  = 0,
         count = last - first;

      while (count > 0)
      {
         step  = count >>> 1;
         index = first + step;

         if (comp (value, array [index]))
         {
            count = step;
         }
         else
         {
            first  = ++ index;
            count -= step + 1;
         }
      }

      return first;
   },
};

const Algorithm_default_ = Algorithm;
;

/* harmony default export */ const Math_Algorithm = (Namespace/* default */.A .add ("Algorithm", Algorithm_default_));
;// ./src/x_ite/Execution/NamedNodesHandling.js
const getUniqueName = function (map, name = "")
{
   name = String (name);

   if (name && !map .has (name))
      return name;

   name = name .replace (/_\d+$/, "");

   if (name && !map .has (name))
      return name;

   let
      newName = "",
      lo,
      hi = 1;

   do
   {
      lo   = hi;
      hi <<= 1;

      newName = `${name}_${lo}`;
   }
   while (map .has (newName));

   lo >>>= 1;
   hi >>>= 1;

   if (lo && hi)
   {
      while (lo < hi)
      {
         const m = (lo + hi) >>> 1;

         if (map .has (`${name}_${m}`))
            lo = m + 1;
         else
            hi = m;
      }

      newName = `${name}_${lo}`;
   }

   return newName;
};

;// ./src/x_ite/InputOutput/X3DGenerator.js




function Generator ({ style = "TIDY", indent = "", indentChar = "  ", precision = 7, doublePrecision = 15, names = true } = { })
{
   this .string          = "";
   this .indent          = indent;
   this .listIndent      = indent;
   this .precision       = Math_Algorithm .clamp (precision, 1, 21);
   this .doublePrecision = Math_Algorithm .clamp (doublePrecision, 1, 21);
   this .outputNames     = names; // private option: used in StaticGroup for toVRMLString.

   this .floatFormat             = this .createFloatFormat (this .precision);
   this .floatExponentialFormat  = this .createFloatExponentialFormat (this .precision);
   this .doubleFormat            = this .createFloatFormat (this .doublePrecision);
   this .doubleExponentialFormat = this .createFloatExponentialFormat (this .doublePrecision);

   this .Style (style, indentChar);

   this .executionContextStack = [ ];
   this .executionContextIndex = new Set ();
   this .importedNodesIndex    = new Map ();
   this .importedNames         = new Map ();
   this .exportedNodesIndex    = new Map ();
   this .nodes                 = new Map ();
   this .names                 = new Map ();
   this .namesByNode           = new Map ();
   this .routeNodes            = new Map ();
}

Object .assign (Generator .prototype,
{
   Style (style, indentChar = "  ")
   {
      switch (style)
      {
         case "CLEAN":
         {
            this .listIndent = "";

            this .comma          = " ";
            this .break          = " ";
            this .tidyBreak      = "";
            this .tidySpace      = "";
            this .indentChar     = "";
            this .listEnclosure  = "";
            this .listBreak      = "";
            this .listIndentChar = "";
            this .attribBreak    = " ";
            break;
         }
         case "SMALL":
         {
            this .listIndent = "";

            this .comma          = ",";
            this .break          = "\n";
            this .tidyBreak      = "\n";
            this .tidySpace      = "";
            this .indentChar     = "";
            this .listEnclosure  = "";
            this .listBreak      = "";
            this .listIndentChar = "";
            this .attribBreak    = " ";
            break;
         }
         case "COMPACT":
         {
            this .listIndent = "";

            this .comma          = ",";
            this .break          = "\n";
            this .tidyBreak      = "\n";
            this .tidySpace      = " ";
            this .indentChar     = indentChar;
            this .listEnclosure  = " ";
            this .listBreak      = " ";
            this .listIndentChar = "";
            this .attribBreak    = " ";
            break;
         }
         case "TIDY":
         default:
         {
            this .comma          = ",";
            this .break          = "\n";
            this .tidyBreak      = "\n";
            this .tidySpace      = " ";
            this .indentChar     = indentChar;
            this .listEnclosure  = "\n";
            this .listBreak      = "\n";
            this .listIndentChar = this .indentChar;
            this .attribBreak    = "\n";
            break;
         }
      }
   },
   Comma ()
   {
      this .string += this .comma;
   },
   Break ()
   {
      this .needsSpace = false;

      this .string += this .break;
   },
   TidyBreak ()
   {
      this .needsSpace &&= !this .tidyBreak;

      this .string += this .tidyBreak;
   },
   Space ()
   {
      this .needsSpace = false;

      this .string += " ";
   },
   TidySpace ()
   {
      this .needsSpace &&= !this .tidySpace;

      this .string += this .tidySpace;
   },
   ListStart ()
   {
      this .string += this .listEnclosure;
   },
   ListEnd ()
   {
      this .string += this .listEnclosure;
   },
   ListBreak ()
   {
      this .string += this .listBreak;
   },
   AttribBreak ()
   {
      this .string += this .attribBreak;
   },
   Indent ()
   {
      this .string += this .indent;
   },
   ListIndent ()
   {
      this .string += this .listIndent;
   },
   IncIndent ()
   {
      this .indent     += this .indentChar;
      this .listIndent += this .listIndentChar;
   },
   DecIndent ()
   {
      this .indent     = this .indent     .slice (0, -this .indentChar     .length);
      this .listIndent = this .listIndent .slice (0, -this .listIndentChar .length);
   },
   NeedsSpace ()
   {
      this .needsSpace = true;
   },
   CheckSpace ()
   {
      if (this .needsSpace)
         this .string += " ";

      this .needsSpace = false;
   },
   createFloatFormat (precision)
   {
      return new Intl .NumberFormat ("en",
      {
         notation: "standard",
         maximumSignificantDigits: precision,
         useGrouping: false,
      })
      .format;
   },
   createFloatExponentialFormat (precision)
   {
      return new Intl .NumberFormat ("en",
      {
         notation: "scientific",
         maximumSignificantDigits: precision,
         useGrouping: false,
      })
      .format;
   },
   FloatFormat  (value)
   {
      if (Number .isFinite (value))
      {
         const exponent = Math .log10 (Math .abs (value));

         if ((this .precision > exponent && exponent >= -4) || value === 0)
            return this .floatFormat (value);

         return this .floatExponentialFormat (value) .toLowerCase ();
      }
      else
      {
         return String (value);
      }
   },
   DoubleFormat  (value)
   {
      if (Number .isFinite (value))
      {
         const exponent = Math .log10 (Math .abs (value));

         if ((this .doublePrecision > exponent && exponent >= -4) || value === 0)
            return this .doubleFormat (value);

         return this .doubleExponentialFormat (value) .toLowerCase ();
      }
      else
      {
         return String (value);
      }
   },
   PushExecutionContext (executionContext)
   {
      this .executionContextStack .push (executionContext);

      if (this .executionContextIndex .has (executionContext))
         return;

      this .executionContextIndex .add (executionContext);
      this .importedNodesIndex    .set (executionContext, new Set ());
      this .importedNames         .set (executionContext, new Map ());
      this .exportedNodesIndex    .set (executionContext, new Set ());
      this .nodes                 .set (executionContext, new Set ());
      this .names                 .set (executionContext, new Set ());
      this .namesByNode           .set (executionContext, new Map ());
      this .routeNodes            .set (executionContext, new Set ());

      this .needsSpace = false;
   },
   PopExecutionContext ()
   {
      const executionContext = this .executionContextStack .pop ();

      if (this .executionContextStack .at (-1) === executionContext)
         return;

      this .executionContextIndex .delete (executionContext);
      this .importedNodesIndex    .delete (executionContext);
      this .importedNames         .delete (executionContext);
      this .exportedNodesIndex    .delete (executionContext);
      this .nodes                 .delete (executionContext);
      this .names                 .delete (executionContext);
      this .namesByNode           .delete (executionContext);
      this .routeNodes            .delete (executionContext);
   },
   ExecutionContext ()
   {
      return this .executionContextStack .at (-1);
   },
   NamedNodes (namedNodes)
   {
      const
         names       = this .names .get (this .ExecutionContext ()),
         namesByNode = this .namesByNode .get (this .ExecutionContext ());

      for (const node of namedNodes)
      {
         if (node .getNodeName () .match (/^_\d+$/))
            continue;

         names .add (node .getNodeName ());
         namesByNode .set (node .getValue (), node .getNodeName ());
      }
   },
   ExportedNodes (exportedNodes)
   {
      const index = this .exportedNodesIndex .get (this .ExecutionContext ());

      for (const exportedNode of exportedNodes)
         index .add (exportedNode .getLocalNode ());
   },
   ImportedNodes (importedNodes)
   {
      const index = this .importedNodesIndex .get (this .ExecutionContext ());

      for (const importedNode of importedNodes)
         index .add (importedNode .getInlineNode ());
   },
   AddRouteNode (routeNode)
   {
      const routeNodes = this .routeNodes .get (this .ExecutionContext ());

      routeNodes .add (routeNode);
   },
   ExistsRouteNode (routeNode)
   {
      const routeNodes = this .routeNodes .get (this .ExecutionContext ());

      return routeNodes .has (routeNode);
   },
   IsSharedNode (baseNode)
   {
      return this .ExecutionContext () !== baseNode .getExecutionContext ();
   },
   AddNode (baseNode)
   {
      const nodes = this .nodes .get (this .ExecutionContext ());

      nodes .add (baseNode);

      this .AddRouteNode (baseNode);
   },
   ExistsNode (baseNode)
   {
      const nodes = this .nodes .get (this .ExecutionContext ());

      return nodes .has (baseNode);
   },
   Name (baseNode)
   {
      // Is the node already in index.

      const
         namesByNode = this .namesByNode .get (this .ExecutionContext ()),
         name        = namesByNode .get (baseNode);

      if (name !== undefined)
      {
         return name;
      }
      else
      {
         const
            names = this .names .get (this .ExecutionContext ()),
            name  = baseNode .getName ();

         // The node has no name.

         if (name .match (/^(?:_\d+)?$/) && !this .NeedsName (baseNode))
            return "";

         const newName = getUniqueName (names, name);

         // Add to indices.

         names .add (newName);
         namesByNode .set (baseNode, newName);

         return newName;
      }
   },
   NeedsName (baseNode)
   {
      if (baseNode .getCloneCount () > 1)
         return true;

      if (baseNode .hasRoutes ())
         return true;

      const executionContext = this .ExecutionContext ();

      if (this .importedNodesIndex .get (executionContext) .has (baseNode))
         return true;

      if (this .exportedNodesIndex .get (executionContext) .has (baseNode))
         return true;

      return false;
   },
   ImportedName (importedNode)
   {
      const importedNames = this .importedNames .get (this .ExecutionContext ());

      if (importedNames .has (importedNode))
         return importedNames .get (importedNode);

      const
         names   = this .names .get (this .ExecutionContext ()),
         newName = getUniqueName (names, importedNode .getImportedName ());

      // Add to indices.

      names .add (newName);
      importedNames .set (importedNode, newName);

      return newName;
   },
   AccessType (accessType)
   {
      switch (accessType)
      {
         case Base_X3DConstants .initializeOnly:
            return "initializeOnly";
         case Base_X3DConstants .inputOnly:
            return "inputOnly";
         case Base_X3DConstants .outputOnly:
            return "outputOnly";
         case Base_X3DConstants .inputOutput:
            return "inputOutput";
      }
   },
   ToUnit (category, value)
   {
      return this .ExecutionContext () ?.toUnit (category, value) ?? value;
   },
});

for (const key of Object .keys (Generator .prototype))
   Object .defineProperty (Generator .prototype, key, { enumerable: false });

const X3DGenerator_default_ = Generator;
;

/* harmony default export */ const X3DGenerator = (Namespace/* default */.A .add ("X3DGenerator", X3DGenerator_default_));
;// ./src/x_ite/InputOutput/VRMLGenerator.js


function VRMLGenerator (options)
{
   X3DGenerator .call (this, options);
}

Object .assign (Object .setPrototypeOf (VRMLGenerator .prototype, X3DGenerator .prototype),
{
   NULL ()
   {
      this .CheckSpace ();
      this .string += "NULL";
      this .NeedsSpace ();
   },
});

for (const key of Object .keys (VRMLGenerator .prototype))
   Object .defineProperty (VRMLGenerator .prototype, key, { enumerable: false });

const VRMLGenerator_default_ = VRMLGenerator;
;

/* harmony default export */ const InputOutput_VRMLGenerator = (Namespace/* default */.A .add ("VRMLGenerator", VRMLGenerator_default_));
;// ./src/x_ite/InputOutput/XMLGenerator.js


function XMLGenerator (options = { })
{
   X3DGenerator .call (this, options);

   const { html = false, closingTags = false } = options;

   this .html            = html;
   this .closingTags     = html || closingTags;
   this .containerFields = [ ];
}

Object .assign (Object .setPrototypeOf (XMLGenerator .prototype, X3DGenerator .prototype),
{
   PushContainerField (field)
   {
      this .containerFields .push (field);
   },
   PopContainerField ()
   {
      this .containerFields .pop ();
   },
   EncodeString: (() =>
   {
      const map = {
         "\\": "\\\\",
         "\r": "&#xD;",
         "\n": "&#xA;",
         "\t": "&#x9;",
         "\"": "\\\"",
         "'": "&apos;",
         "<": "&lt;",
         ">": "&gt;",
         "&": "&amp;",
      };

      const regex = /([\\\r\n\t"'<>&])/g;

      return function (string)
      {
         return String (string) .replace (regex, char => map [char]);
      };
   })(),
   EncodeSourceText: (() =>
   {
      const map = {
         "\\": "\\\\",
         "\"": "\\\"",
         "'": "&apos;",
         "<": "&lt;",
         ">": "&gt;",
         "&": "&amp;",
      };

      const regex = /([\\"'<>&])/g;

      return function (string)
      {
         return string .replace (regex, char => map [char]);
      };
   })(),
   openingTag (name)
   {
      this .Indent ();
      this .string += "<";
      this .string += name;
      this .string += ">";
   },
   closingTag (name)
   {
      this .Indent ();
      this .string += "</";
      this .string += name;
      this .string += ">";
   },
   openTag (name)
   {
      this .Indent ();
      this .string += "<";
      this .string += name;
   },
   endTag ()
   {
      this .string += ">";
      this .TidyBreak ();
   },
   closeTag (name)
   {
      if (this .closingTags)
      {
         this .string += "></";
         this .string += name;
         this .string += ">";
      }
      else
      {
         this .string += "/>";
      }
   },
   attribute (name, value)
   {
      this .Space ();
      this .string += name;
      this .string += "='";
      this .string += this .EncodeString (value);
      this .string += "'";
   },
   containerField (defaultContainerField)
   {
      const containerField = this .containerFields .at (-1);

      if (!containerField)
         return;

      if (containerField .getName () !== defaultContainerField)
         this .attribute ("containerField", containerField .getName ());
   },
});

for (const key of Object .keys (XMLGenerator .prototype))
   Object .defineProperty (XMLGenerator .prototype, key, { enumerable: false });

const XMLGenerator_default_ = XMLGenerator;
;

/* harmony default export */ const InputOutput_XMLGenerator = (Namespace/* default */.A .add ("XMLGenerator", XMLGenerator_default_));
;// ./src/x_ite/InputOutput/JSONGenerator.js


function JSONGenerator (options)
{
   X3DGenerator .call (this, options);
}

Object .assign (Object .setPrototypeOf (JSONGenerator .prototype, X3DGenerator .prototype),
{
   EncodeString: (() =>
   {
      const map = {
         "\\": "\\\\",
         "\r": "\\r",
         "\n": "\\n",
         "\t": "\\t",
         "\"": "\\\"",
      };

      const regex = /([\\\t\r\n"])/g;

      return function (string)
      {
         return String (string) .replace (regex, char => map [char]);
      };
   })(),
   Number (value)
   {
      switch (value)
      {
         case "NaN":
         case "Infinity":
         case "-Infinity":
            return '"' + value + '"';
         default:
            return value;
      }
   },
   RemoveComma ()
   {
      this .string = this .string .replace (/,\s*$/s, "");
   },
   stringProperty (key, value, comma = true)
   {
      if (comma)
         this .string += ',';

      this .TidyBreak ();
      this .Indent ();
      this .string += '"';
      this .string += key;
      this .string += '"';
      this .string += ':';
      this .TidySpace ();
      this .string += '"';
      this .string += this .EncodeString (value);
      this .string += '"';
   },
   numberProperty (key, value, comma = true)
   {
      if (comma)
         this .string += ',';

      this .TidyBreak ();
      this .Indent ();
      this .string += '"';
      this .string += key;
      this .string += '"';
      this .string += ':';
      this .TidySpace ();
      this .string += value;
   },
   beginObject (name, comma = true, main = false)
   {
      if (comma)
         this .string += ',';

      if (name)
      {
         if (main)
         {
            this .string += '{';
            this .TidySpace ();
            this .string += '"';
            this .string += name;
            this .string += '"';
            this .string += ':';
            this .TidyBreak ();
            this .IncIndent ();
            this .Indent ();
            this .string += '{';
            this .IncIndent ();
         }
         else
         {
            this .TidyBreak ();
            this .Indent ();
            this .string += '"';
            this .string += name;
            this .string += '"';
            this .string += ':';
            this .TidySpace ();
            this .string += '{';
            this .IncIndent ();
         }
      }
      else
      {
         this .TidyBreak ();
         this .Indent ();
         this .string += '{';
         this .IncIndent ();
      }
   },
   endObject ()
   {
      this .TidyBreak ();
      this .DecIndent ();
      this .Indent ();
      this .string += '}';
   },
   beginArray (name, comma = true)
   {
      this .beginValue (name, comma);

      this .string += '[';
      this .IncIndent ();
   },
   endArray ()
   {
      this .TidyBreak ();
      this .DecIndent ();
      this .Indent ();
      this .string += ']';
   },
   beginValue (name, comma = true)
   {
      if (comma)
         this .string += ',';

      this .TidyBreak ();
      this .Indent ();
      this .string += '"';
      this .string += name;
      this .string += '"';
      this .string += ':';
      this .TidySpace ();
   },
});

for (const key of Object .keys (JSONGenerator .prototype))
   Object .defineProperty (JSONGenerator .prototype, key, { enumerable: false });

const JSONGenerator_default_ = JSONGenerator;
;

/* harmony default export */ const InputOutput_JSONGenerator = (Namespace/* default */.A .add ("JSONGenerator", JSONGenerator_default_));
;// ./src/x_ite/DEVELOPMENT.js
// Modified during dist build.

const DEVELOPMENT_default_ = false;
;

/* harmony default export */ const DEVELOPMENT = (Namespace/* default */.A .add ("DEVELOPMENT", DEVELOPMENT_default_));
;// ./src/x_ite/Features.js


const MODULE = true;

const Features =
{
   get ENVIRONMENT ()
   {
      if (DEVELOPMENT)
         return "BROWSER";

      if (true)
         return "MODULE";

      // removed by dead control flow


      // removed by dead control flow

   },
   WEAK_REF: typeof WeakRef !== "undefined",
   FINALIZATION_REGISTRY: typeof FinalizationRegistry !== "undefined",
};

(() =>
{
   // Added at February 2022
   // https://github.com/tc39/proposal-relative-indexing-method#polyfill

   function at (n)
   {
      // ToInteger() abstract op
      n = Math.trunc(n) || 0;
      // Allow negative indexing from the end
      if (n < 0) n += this.length;
      // OOB access is guaranteed to return undefined
      if (n < 0 || n >= this.length) return undefined;
      // Otherwise, this is just normal property access
      return this[n];
   }

   const TypedArray = Reflect .getPrototypeOf (Int8Array);
   for (const C of [Array, String, TypedArray])
   {
      if (C .prototype .at === undefined)
      {
         Object .defineProperty (C .prototype, "at",
         {
            value: at,
            writable: true,
            enumerable: false,
            configurable: true,
         });
      }
   }
})();

(() =>
{
   if (!Features .WEAK_REF)
   {
      if (DEVELOPMENT)
         console .info ("Added shim for WeakRef.");

      window .WeakRef = class WeakRef
      {
         #object;

         constructor (object)
         {
            this .#object = object;
         }

         deref ()
         {
            return this .#object;
         }
      };
   }

   if (!Features .FINALIZATION_REGISTRY)
   {
      if (DEVELOPMENT)
         console .info ("Added shim for FinalizationRegistry.");

      window .FinalizationRegistry = class FinalizationRegistry
      {
         register ()
         { }

         unregister ()
         { }
      };
   }

   if (typeof Float16Array === "undefined")
      window .Float16Array = Uint16Array;
})();

(() =>
{
   // https://tc39.es/proposal-upsert/

   for (const Class of [Map, WeakMap])
   {
      Class .prototype .getOrInsert ??= function (key, value)
      {
         if (this .has (key))
            return this .get (key);

         this .set (key, value);

         return value;
      };
   }

   for (const Class of [Map, WeakMap])
   {
      Class .prototype .getOrInsertComputed ??= function (key, callbackfn)
      {
         if (this .has (key))
            return this .get (key);

         const value = callbackfn (key);

         this .set (key, value);

         return value;
      };
   }
})();

(() =>
{
   // Added June 2026
   if (Math .sumPrecise === undefined)
   {
      Object .defineProperty (Math, "sumPrecise",
      {
         value (iterable)
         {
            return Array .from (iterable) .reduce ((p, c) => p + c, 0);
         },
         writable: true,
         enumerable: false,
         configurable: true,
      });
   }
})();

const Features_default_ = Features;
;

/* harmony default export */ const x_ite_Features = (Namespace/* default */.A .add ("Features", Features_default_));
;// ./src/x_ite/Base/X3DObject.js
/* provided dependency */ var $ = __webpack_require__(44).A;





const
   _id            = Symbol (),
   _name          = Symbol (),
   _interests     = Symbol (),
   _registry      = Symbol (),
   _userData      = Symbol (),
   _vrmlGenerator = Symbol (),
   _xmlGenerator  = Symbol (),
   _jsonGenerator = Symbol ();

function X3DObject () { }

Object .assign (X3DObject .prototype,
{
   [_id]: undefined,
   [_name]: "",
   [_interests]: null,
   [_registry]: null,
   [_userData]: null,
   getId ()
   {
      return this [_id] ??= X3DObject .getId (this);
   },
   getTypeName ()
   {
      return this .constructor .typeName;
   },
   setName (value)
   {
      this [_name] = value;
   },
   getName ()
   {
      return this [_name];
   },
   getDisplayName ()
   {
      return this [_name];
   },
   hasInterest (callbackName, object)
   {
      return this [_interests] ?.has (X3DObject .getInterestId (object [callbackName], object)) ?? false;
   },
   addInterest (callbackName, object, ... args)
   {
      const
         weakRef    = new WeakRef (object),
         callback   = object [callbackName],
         interestId = X3DObject .getInterestId (callback, object);

      this [_registry] ??= new FinalizationRegistry (objectId =>
      {
         for (const interestId of this [_interests] .keys ())
         {
            if (interestId .endsWith (objectId))
               this [_interests] .delete (interestId);
         }
      });

      this [_interests] = new Map (this [_interests]);

      this [_registry] .register (object, interestId .replace (/^[^:]+/, ""), object);
      this [_interests] .set (interestId, { callback, weakRef, args });
   },
   removeInterest (callbackName, object)
   {
      this [_interests] ?.delete (X3DObject .getInterestId (object [callbackName], object));
   },
   getInterests ()
   {
      return this [_interests] ??= new Map ();
   },
   processInterests: (() =>
   {
      const EMPTY = [ ];

      return function ()
      {
         for (const { callback, weakRef, args } of this [_interests] ?.values () ?? EMPTY)
         {
            const object = weakRef .deref ();

            if (object)
               callback .call (object, ... args, this);
         }
      };
   })(),
   getUserData (key)
   {
      return this [_userData] ?.get (key);
   },
   setUserData (key, value)
   {
      this [_userData] ??= new Map ();

      this [_userData] .set (key, value);
   },
   removeUserData (key)
   {
      this [_userData] ?.delete (key);
   },
   toString (options)
   {
      const generator = !options || $.isEmptyObject (options)
         ? X3DObject [_vrmlGenerator] ??= new InputOutput_VRMLGenerator ()
         : new InputOutput_VRMLGenerator (options);

      generator .string = "";

      generator .PushExecutionContext (options ?.scene ?? null);

      this .toStream (generator);

      generator .PopExecutionContext ();

      return generator .string;
   },
   toVRMLString (options)
   {
      const generator = !options || $.isEmptyObject (options)
         ? X3DObject [_vrmlGenerator] ??= new InputOutput_VRMLGenerator ()
         : new InputOutput_VRMLGenerator (options);

      generator .string = "";

      generator .PushExecutionContext (options ?.scene ?? null);

      this .toVRMLStream (generator);

      generator .PopExecutionContext ();

      return generator .string;
   },
   toXMLString (options)
   {
      const generator = !options || $.isEmptyObject (options)
         ? X3DObject [_xmlGenerator] ??= new InputOutput_XMLGenerator ()
         : new InputOutput_XMLGenerator (options);

      generator .string = "";

      generator .PushExecutionContext (options ?.scene ?? null);

      this .toXMLStream (generator);

      generator .PopExecutionContext ();

      return generator .string;
   },
   toJSONString (options)
   {
      const generator = !options || $.isEmptyObject (options)
         ? X3DObject [_jsonGenerator] ??= new InputOutput_JSONGenerator ()
         : new InputOutput_JSONGenerator (options);

      generator .string = "";

      generator .PushExecutionContext (options ?.scene ?? null);

      this .toJSONStream (generator);

      generator .PopExecutionContext ();

      return generator .string;
   },
   toStream (generator)
   {
      generator .string = Object .prototype .toString .call (this);
   },
   dispose ()
   {
      this [_interests] ?.clear ();
      this [_userData]  ?.clear ();
   },
});

for (const key of Object .keys (X3DObject .prototype))
   Object .defineProperty (X3DObject .prototype, key, { enumerable: false });

Object .defineProperties (X3DObject .prototype,
{
   [Symbol .toStringTag]:
   {
      get () { return this .getTypeName (); },
   },
   // using and Symbol.dispose are not yet supported by Safari.
   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using
   // [Symbol .dispose]:
   // {
   //    get () { this .dispose (); },
   // },
});

Object .assign (X3DObject,
{
   [_vrmlGenerator]: null,
   [_xmlGenerator]: null,
   [_jsonGenerator]: null,
   getId: x_ite_Features .FINALIZATION_REGISTRY ? (() =>
   {
      const
         map      = new WeakMap (),
         cache    = [ ],
         registry = new FinalizationRegistry (id => cache .push (id));

      let counter = 0;

      return function (object)
      {
         const id = map .get (object);

         if (id !== undefined)
         {
            return id;
         }
         else
         {
            const id = cache .pop () ?? ++ counter;

            map .set (object, id);
            registry .register (object, id);

            return id;
         }
      };
   })() : (() =>
   {
      const map = new WeakMap ();

      let counter = 0;

      return function (object)
      {
         const id = map .get (object);

         if (id !== undefined)
            return id;

         map .set (object, ++ counter);

         return counter;
      };
   })(),
   getInterestId (callback, object)
   {
      return `${this .getId (callback)}:${this .getId (object)}`;
   },
});

const X3DObject_default_ = X3DObject;
;

/* harmony default export */ const Base_X3DObject = (Namespace/* default */.A .add ("X3DObject", X3DObject_default_));
;// ./src/x_ite/Base/IterableWeakSet.js


const IterableWeakSet_default_ = x_ite_Features .WEAK_REF && x_ite_Features .FINALIZATION_REGISTRY ?
/**
 * IterableWeakSet is a writable set-like class.
 */
class IterableWeakSet
{
   #map      = new Map ();
   #registry = new FinalizationRegistry (id => this .#map .delete (id));

   constructor (objects)
   {
      if (objects)
      {
         for (const object of objects)
            this .add (object);
      }
   }

   *[Symbol .iterator] ()
   {
      yield* this .values ();
   }

   get size ()
   {
      return this .#map .size;
   }

   add (object)
   {
      this .#map .set (object .getId (), new WeakRef (object));
      this .#registry .register (object, object .getId (), object);
   }

   clear ()
   {
      for (const object of this .values ())
         this .#registry .unregister (object);

      this .#map .clear ();
   }

   delete (object)
   {
      this .#map .delete (object .getId ());
      this .#registry .unregister (object);
   }

   forEach (callbackFn, thisArg)
   {
      for (const object of this .values ())
         callbackFn .call (thisArg, object, object, this);
   }

   has (object)
   {
      return this .#map .has (object .getId ());
   }

   *entries ()
   {
      for (const object of this .values ())
         yield [object, object];
   }

   *keys ()
   {
      yield *this .values ();
   }

   *values ()
   {
      for (const weakRef of this .#map .values ())
      {
         const object = weakRef .deref ();

         if (object)
            yield object;
      }
   }
}
:
class IterableWeakSet extends Set
{
   constructor (objects)
   {
      super (objects);
   }
};
;

/* harmony default export */ const IterableWeakSet = (Namespace/* default */.A .add ("IterableWeakSet", IterableWeakSet_default_));
;// ./src/x_ite/Base/X3DChildObject.js



const
   _modificationTime = Symbol (),
   _tainted          = Symbol (),
   _parents          = Symbol (),
   _private          = Symbol ();

const EMPTY = [ ];

function X3DChildObject ()
{
   Base_X3DObject .call (this);
}

Object .assign (Object .setPrototypeOf (X3DChildObject .prototype, Base_X3DObject .prototype),
{
   [_modificationTime]: -1,
   [_tainted]: false,
   [_parents]: null,
   [_private]: false,
   isInitializable ()
   {
      return true;
   },
   isInput ()
   {
      return false;
   },
   isOutput ()
   {
      return false;
   },
   setModificationTime (value)
   {
      this [_modificationTime] = value;
   },
   getModificationTime ()
   {
      return this [_modificationTime];
   },
   setTainted (value)
   {
      this [_tainted] = value;
   },
   isTainted ()
   {
      return this [_tainted];
   },
   addEvent ()
   {
      for (const parent of this [_parents] ?? EMPTY)
         parent .addEvent (this);
   },
   addEventObject (field, event)
   {
      for (const parent of this [_parents] ?? EMPTY)
         parent .addEventObject (this, event);
   },
   processEvent ()
   {
      this .setTainted (false);
      this .processInterests ();
   },
   isPrivate ()
   {
      return this [_private];
   },
   setPrivate (value)
   {
      this [_private] = value;
   },
   collectCloneCount ()
   {
      let cloneCount = 0;

      for (const parent of this [_parents] ?? EMPTY)
      {
         if (parent [_private])
            continue;

         cloneCount += parent .collectCloneCount ();
      }

      return cloneCount;
   },
   addParent (parent)
   {
      this .getParents () .add (parent);

      this .parentsChanged ();
   },
   removeParent (parent)
   {
      this [_parents] ?.delete (parent);

      this .parentsChanged ();
   },
   getParents ()
   {
      return this [_parents] ??= new IterableWeakSet ();
   },
   parentsChanged ()
   { },
   dispose ()
   {
      this [_parents] ?.clear ();

      Base_X3DObject .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DChildObject .prototype))
   Object .defineProperty (X3DChildObject .prototype, key, { enumerable: false });

const X3DChildObject_default_ = X3DChildObject;
;

/* harmony default export */ const Base_X3DChildObject = (Namespace/* default */.A .add ("X3DChildObject", X3DChildObject_default_));
;// ./src/x_ite/Base/Events.js
const Events = Object .assign ([ ],
{
   create (field)
   {
      const event = this .pop () ?? new Set ();

      event .field = field;
      event .clear ();

      return event;
   },
   copy (event)
   {
      const copy = this .create (event .field);

      for (const source of event)
         copy .add (source);

      return copy;
   },
   from (field)
   {
      const event = this .create (field);

      event .add (field);

      return event;
   },
});

for (const key of Object .keys (Events))
   Object .defineProperty (Events, key, { enumerable: false });

const Events_default_ = Events;
;

/* harmony default export */ const Base_Events = (Namespace/* default */.A .add ("Events", Events_default_));
;// ./src/x_ite/Base/X3DFieldDefinition.js


const
   _accessType = Symbol (),
   X3DFieldDefinition_name       = Symbol (),
   _value      = Symbol ();

function X3DFieldDefinition (accessType, name, value)
{
   this [_accessType] = accessType;
   this [X3DFieldDefinition_name]       = name;
   this [_value]      = value;
}

Object .assign (Object .setPrototypeOf (X3DFieldDefinition .prototype, Base_X3DObject .prototype),
{
   getAccessType ()
   {
      return this [_accessType];
   },
   getDataType ()
   {
      return this [_value] .getType ();
   },
   getName ()
   {
      return this [X3DFieldDefinition_name];
   },
   getValue ()
   {
      return this [_value];
   },
   getAppInfo ()
   {
      return this [_value] .getAppInfo ();
   },
   setAppInfo (value)
   {
      return this [_value] .setAppInfo (value);
   },
   getDocumentation ()
   {
      return this [_value] .getDocumentation ();
   },
   setDocumentation (value)
   {
      this [_value] .setDocumentation (value);
   },
});

for (const key of Object .keys (X3DFieldDefinition .prototype))
   Object .defineProperty (X3DFieldDefinition .prototype, key, { enumerable: false });

Object .defineProperties (X3DFieldDefinition .prototype,
{
   accessType:
   {
      get: X3DFieldDefinition .prototype .getAccessType,
      enumerable: true,
   },
   dataType:
   {
      get: X3DFieldDefinition .prototype .getDataType,
      enumerable: true,
   },
   name:
   {
      get: X3DFieldDefinition .prototype .getName,
      enumerable: true,
   },
   value:
   {
      get () { return this .getValue () .valueOf (); },
      enumerable: true,
   },
   appInfo:
   {
      get: X3DFieldDefinition .prototype .getAppInfo,
      set: X3DFieldDefinition .prototype .setAppInfo,
      enumerable: true,
   },
   documentation:
   {
      get: X3DFieldDefinition .prototype .getDocumentation,
      set: X3DFieldDefinition .prototype .setDocumentation,
      enumerable: true,
   },
});

Object .defineProperties (X3DFieldDefinition,
{
   typeName:
   {
      value: "X3DFieldDefinition",
      enumerable: true,
   },
});

const X3DFieldDefinition_default_ = X3DFieldDefinition;
;

/* harmony default export */ const Base_X3DFieldDefinition = (Namespace/* default */.A .add ("X3DFieldDefinition", X3DFieldDefinition_default_));
;// ./src/x_ite/Base/X3DInfoArray.js


const
   _array     = Symbol (),
   _index     = Symbol (),
   _valueType = Symbol ();

const handler =
{
   get (target, key, receiver)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
            return target [_array] [index];
      }

      return Reflect .get (target, key, receiver);
   },
   set (target, key, value, receiver)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
            return false;
      }

      return Reflect .set (target, key, value, receiver);
   },
   has (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
            return index < target [_array] .length;
      }

      return Reflect .has (target, key);
   },
   ownKeys (target)
   {
      return Object .keys (target [_array]) .concat (Reflect .ownKeys (target));
   },
   getOwnPropertyDescriptor (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            if (index < target [_array] .length)
            {
               const propertyDescriptor = Object .getOwnPropertyDescriptor (target [_array], key);

               if (propertyDescriptor)
                  propertyDescriptor .writable = false;

               return propertyDescriptor;
            }
         }
      }

      return Reflect .getOwnPropertyDescriptor (target, key);
   },
};

function X3DInfoArray (values, valueType)
{
   const proxy = new Proxy (this, handler);

   Base_X3DChildObject .call (this);

   this [_array]     = [ ];
   this [_index]     = new Map ();
   this [_valueType] = valueType;

   for (const [key, value] of values)
      this .add (key, value);

   return proxy;
}

Object .assign (Object .setPrototypeOf (X3DInfoArray .prototype, Base_X3DChildObject .prototype),
{
   *[Symbol .iterator] ()
   {
      yield* this [_array];
   },
   copy ()
   {
      const copy = new (this .constructor) ();

      copy .assign (this);

      return copy;
   },
   clear ()
   {
      this [_array] .length = 0;
      this [_index] .clear ();

      this .addEvent ();
   },
   assign (array)
   {
      if (!(array instanceof this .constructor))
         throw new Error ("Couldn't assign info array, wrong type.");

      this [_array] = Array .from (array [_array]);
      this [_index] = new Map (array [_index]);

      this .addEvent ();
   },
   equals (array)
   {
      const
         a      = this [_array],
         b      = array [_array],
         length = a .length;

      if (a === b)
         return true;

      if (length !== b .length)
         return false;

      for (let i = 0; i < length; ++ i)
      {
         if (a [i] !== b [i])
            return false;
      }

      return true;
   },
   has (key)
   {
      return this [_index] .has (key);
   },
   get (key)
   {
      return this [_index] .get (key);
   },
   add (key, value)
   {
      if (this [_index] .has (key))
         throw new Error (`Couldn't add value to ${this .getTypeName ()}, key '${key}' already exists.`);

      if (!(value instanceof this [_valueType]))
         throw new Error (`Couldn't add value to ${this .getTypeName ()}, value for key '${key}' has wrong type.`);

      this [_array] .push (value);
      this [_index] .set (key, value);

      this .addEvent ();
   },
   alias (alias, value)
   {
      this [_index] .set (alias, value);

      this .addEvent ();
   },
   update (oldKey, newKey, value)
   {
      // TODO: update alias.

      if (!(value instanceof this [_valueType]))
         throw new Error (`Couldn't update value of ${this .getTypeName ()}, value for key '${key}' has wrong type.`);

      const oldValue = this [_index] .get (oldKey);

      if (oldKey !== newKey)
         this .remove (newKey);

      this [_index] .delete (oldKey);
      this [_index] .set (newKey, value);

      if (oldValue !== undefined)
      {
         const index = this [_array] .indexOf (oldValue);

         if (index > -1)
            this [_array] [index] = value;
      }
      else
      {
         this [_array] .push (value);
      }

      this .addEvent ();
   },
   remove (key)
   {
      // TODO: remove alias.

      const value = this [_index] .get (key);

      if (value === undefined)
         return;

      const index = this [_array] .indexOf (value);

      this [_index] .delete (key);

      if (index > -1)
         this [_array] .splice (index, 1);

      this .addEvent ();
   },
   at: Array .prototype .at,
   // concat: Array .prototype .concat,
   // copyWithin: Array.prototype.copyWithin,
   entries: Array .prototype .entries,
   every: Array .prototype .every,
   // fill: Array .prototype .fill,
   filter (callbackFn, thisArg)
   {
      return new (this .constructor) (Array .prototype .filter .call (this, callbackFn, thisArg));
   },
   find: Array .prototype .find,
   findIndex: Array .prototype .findIndex,
   findLast: Array .prototype .findLast,
   findLastIndex: Array .prototype .findLastIndex,
   flat: Array .prototype .flat,
   flatMap: Array .prototype .flatMap,
   forEach: Array .prototype .forEach,
   includes: Array .prototype .includes,
   indexOf: Array .prototype .indexOf,
   join: Array .prototype .join,
   keys: Array .prototype .keys,
   lastIndexOf: Array .prototype .lastIndexOf,
   map (callbackFn, thisArg)
   {
      return new (this .constructor) (Array .prototype .map .call (this, callbackFn, thisArg));
   },
   reduce: Array .prototype .reduce,
   reduceRight: Array .prototype .reduceRight,
   // reverse: Array .prototype .reverse,
   slice (start, end)
   {
      return new (this .constructor) (Array .prototype .slice .call (this, start, end));
   },
   some: Array .prototype .some,
   // sort: Array .prototype .sort,
   toReversed ()
   {
      return new (this .constructor) ([... this] .reverse ());
   },
   toSorted (compareFn)
   {
      return new (this .constructor) ([... this] .sort (compareFn));
   },
   toSpliced (start, deleteCount, ... insertValues)
   {
      const array = [... this];

      array .splice (start, deleteCount, ... insertValues)

      return new (this .constructor) (array);
   },
   values: Array .prototype .values,
   with (index, value)
   {
      const array = [... this];

      array [index] = value;

      return new (this .constructor) (array);
   },
   toVRMLStream (generator)
   {
      const proto = this .getTypeName () .includes ("Proto");

      for (const value of this [_array])
      {
         try
         {
            value .toVRMLStream (generator);

            generator .Break ();

            if (proto)
               generator .TidyBreak ();
         }
         catch (error)
         {
            // console .error (error);
         }
      }
   },
   toXMLStream (generator)
   {
      for (const value of this [_array])
      {
         try
         {
            value .toXMLStream (generator);

            generator .TidyBreak ();
         }
         catch (error)
         {
            // console .error (error);
         }
      }
   },
   toJSONStream (generator, comma = false)
   {
      for (const value of this [_array])
      {
         try
         {
            if (comma)
               generator .string += ',';

            value .toJSONStream (generator, true);

            comma = true;
         }
         catch
         {
            generator .RemoveComma ();
         }
      }

      return comma;
   },
});

for (const key of Object .keys (X3DInfoArray .prototype))
   Object .defineProperty (X3DInfoArray .prototype, key, { enumerable: false });

Object .defineProperties (X3DInfoArray .prototype,
{
   length:
   {
      get () { return this [_array] .length; },
   },
});

const X3DInfoArray_default_ = X3DInfoArray;
;

/* harmony default export */ const Base_X3DInfoArray = (Namespace/* default */.A .add ("X3DInfoArray", X3DInfoArray_default_));
;// ./src/x_ite/Base/FieldDefinitionArray.js



function FieldDefinitionArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .name, value]), Base_X3DFieldDefinition);
}

Object .setPrototypeOf (FieldDefinitionArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (FieldDefinitionArray .prototype))
   Object .defineProperty (FieldDefinitionArray .prototype, key, { enumerable: false });

Object .defineProperties (FieldDefinitionArray,
{
   typeName:
   {
      value: "FieldDefinitionArray",
      enumerable: true,
   },
});

const FieldDefinitionArray_default_ = FieldDefinitionArray;
;

/* harmony default export */ const Base_FieldDefinitionArray = (Namespace/* default */.A .add ("FieldDefinitionArray", FieldDefinitionArray_default_));
;// ./src/x_ite/Base/X3DField.js





const
   X3DField_value               = Symbol (),
   X3DField_accessType          = Symbol (),
   _unit                = Symbol (),
   _references          = Symbol (),
   _referencesCallbacks = Symbol (),
   _fieldInterests      = Symbol (),
   _fieldCallbacks      = Symbol (),
   _inputRoutes         = Symbol (),
   _outputRoutes        = Symbol (),
   _routeCallbacks      = Symbol (),
   _appInfo             = Symbol (),
   _documentation       = Symbol (),
   _uniformLocation     = Symbol .for ("X_ITE.X3DField.uniformLocation");

const X3DField_EMPTY = [ ];

function X3DField (value)
{
   Base_X3DChildObject .call (this);

   this [X3DField_value] = value;
}

Object .assign (Object .setPrototypeOf (X3DField .prototype, Base_X3DChildObject .prototype),
{
   [X3DField_value]: undefined,
   [X3DField_accessType]: Base_X3DConstants .initializeOnly,
   [_unit]: undefined,
   [_references]: null,
   [_referencesCallbacks]: null,
   [_fieldInterests]: null,
   [_fieldCallbacks]: null,
   [_inputRoutes]: null,
   [_outputRoutes]: null,
   [_routeCallbacks]: null,
   [_uniformLocation]: null,
   [_appInfo]: "",
   [_documentation]: "",
   create ()
   {
      return new (this .constructor) ();
   },
   equals (field)
   {
      return this [X3DField_value] === field .valueOf ();
   },
   assign (field)
   {
      // Assigns field to this field.
      this .set (field .getValue ());
      this .addEvent ();
   },
   set (value)
   {
      // Sets internal value without generating event.
      this [X3DField_value] = value;
   },
   setValue (value)
   {
      // Sets internal value and generates event.
      this .set (value instanceof this .constructor ? value .getValue () : value);
      this .addEvent ();
   },
   getValue ()
   {
      return this [X3DField_value];
   },
   getType ()
   {
      return this .constructor .type;
   },
   setAccessType (value)
   {
      this [X3DField_accessType] = value;
   },
   getAccessType ()
   {
      return this [X3DField_accessType];
   },
   isInitializable ()
   {
      return !! (this [X3DField_accessType] & Base_X3DConstants .initializeOnly);
   },
   isInput ()
   {
      return !! (this [X3DField_accessType] & Base_X3DConstants .inputOnly);
   },
   isOutput ()
   {
      return !! (this [X3DField_accessType] & Base_X3DConstants .outputOnly);
   },
   isReadable ()
   {
      return this [X3DField_accessType] !== Base_X3DConstants .inputOnly;
   },
   isWritable ()
   {
      return this [X3DField_accessType] !== Base_X3DConstants .outputOnly;
   },
   setUnit (value)
   {
      this [_unit] = value;
   },
   getUnit ()
   {
      return this [_unit];
   },
   isReference (accessType)
   {
      return accessType === this [X3DField_accessType] || accessType === Base_X3DConstants .inputOutput;
   },
   addReference (reference)
   {
      const references = this .getReferences ();

      if (references .has (reference))
         return;

      references .add (reference);

      // Create IS relationship

      switch (this .getAccessType () & reference .getAccessType ())
      {
         case Base_X3DConstants .initializeOnly:
            reference .addFieldInterest (this);
            this .processEvent (Base_Events .from (reference));
            break;
         case Base_X3DConstants .inputOnly:
            reference .addFieldInterest (this);
            break;
         case Base_X3DConstants .outputOnly:
            this .addFieldInterest (reference);
            break;
         case Base_X3DConstants .inputOutput:
            reference .addFieldInterest (this);
            this .addFieldInterest (reference);
            this .processEvent (Base_Events .from (reference));
            break;
      }

      this .processReferencesCallbacks ();
   },
   removeReference (reference)
   {
      this [_references] ?.delete (reference);

      // Create IS relationship

      switch (this .getAccessType () & reference .getAccessType ())
      {
         case Base_X3DConstants .initializeOnly:
            reference .removeFieldInterest (this);
            break;
         case Base_X3DConstants .inputOnly:
            reference .removeFieldInterest (this);
            break;
         case Base_X3DConstants .outputOnly:
            this .removeFieldInterest (reference);
            break;
         case Base_X3DConstants .inputOutput:
            reference .removeFieldInterest (this);
            this .removeFieldInterest (reference);
            break;
      }

      this .processReferencesCallbacks ();
   },
   getReferences ()
   {
      return this [_references] ??= new IterableWeakSet ();
   },
   addReferencesCallback (key, object)
   {
      this [_referencesCallbacks] = new Map (this [_referencesCallbacks]);

      this [_referencesCallbacks] .set (key, object);
   },
   removeReferencesCallback (key)
   {
      this [_referencesCallbacks] ?.delete (key);
   },
   getReferencesCallbacks ()
   {
      return this [_referencesCallbacks] ??= new Map ();
   },
   processReferencesCallbacks ()
   {
      for (const callback of this [_referencesCallbacks] ?.values () ?? X3DField_EMPTY)
         callback ();
   },
   addFieldInterest (field)
   {
      // There must be no copy, because the event is not executed immediately.

      this .getFieldInterests () .add (field);
   },
   removeFieldInterest (field)
   {
      this [_fieldInterests] ?.delete (field);
   },
   getFieldInterests ()
   {
      return this [_fieldInterests] ??= new IterableWeakSet ();
   },
   addFieldCallback (key, object)
   {
      this [_fieldCallbacks] = new Map (this [_fieldCallbacks]);

      this [_fieldCallbacks] .set (key, object);
   },
   removeFieldCallback (key)
   {
      this [_fieldCallbacks] ?.delete (key);
   },
   getFieldCallbacks ()
   {
      return this [_fieldCallbacks] ??= new Map ();
   },
   addInputRoute (route)
   {
      this .getInputRoutes () .add (route);
      this .processRouteCallbacks ();
   },
   removeInputRoute (route)
   {
      this [_inputRoutes] ?.delete (route);

      this .processRouteCallbacks ();
   },
   getInputRoutes ()
   {
      return this [_inputRoutes] ??= new IterableWeakSet ();
   },
   addOutputRoute (route)
   {
      this .getOutputRoutes () .add (route);
      this .processRouteCallbacks ();
   },
   removeOutputRoute (route)
   {
      this [_outputRoutes] ?.delete (route);

      this .processRouteCallbacks ();
   },
   getOutputRoutes ()
   {
      return this [_outputRoutes] ??= new IterableWeakSet ();
   },
   addRouteCallback (key, object)
   {
      // Processed if routes are changed.

      this [_routeCallbacks] = new Map (this [_routeCallbacks]);

      this [_routeCallbacks] .set (key, object);
   },
   removeRouteCallback (key)
   {
      this [_routeCallbacks] ?.delete (key);
   },
   getRouteCallbacks ()
   {
      return this [_routeCallbacks] ??= new Map ();
   },
   processRouteCallbacks ()
   {
      for (const callback of this [_routeCallbacks] ?.values () ?? X3DField_EMPTY)
         callback ();
   },
   processEvent (event = Base_Events .create (this))
   {
      event .add (this);

      this .setTainted (false);

      const field = event .field;

      if (field !== this)
         this .set (field .getValue (), field .length);

      // Process interests.

      this .processInterests ();

      // Process routes.

      let first = true;

      for (const field of this [_fieldInterests] ?? X3DField_EMPTY)
      {
         if (event .has (field))
            continue;

         if (first)
         {
            first = false;
            field .addEventObject (this, event);
         }
         else
         {
            field .addEventObject (this, Base_Events .copy (event));
         }
      }

      if (first)
         Base_Events .push (event);

      // Process field callbacks.

      for (const callback of this [_fieldCallbacks] ?.values () ?? X3DField_EMPTY)
         callback (this .valueOf ());
   },
   getAppInfo ()
   {
      return this [_appInfo];
   },
   setAppInfo (value)
   {
      this [_appInfo] = String (value);
   },
   setDocumentation (value)
   {
      this [_documentation] = String (value);
   },
   getDocumentation ()
   {
      return this [_documentation];
   },
   fromString (value, scene)
   {
      this .fromVRMLString (value, scene);
   },
   fromVRMLString (value, scene)
   {
      // Function will be overridden in VRMLParser.
   },
   fromXMLString (value, scene)
   {
      // Function will be overridden in XMLParser.
   },
   fromJSONString (value, scene)
   {
      this .fromVRMLString (value, scene);
   },
   dispose ()
   {
      for (const reference of this [_references] ?? X3DField_EMPTY)
         reference .removeFieldInterest (this);

      for (const route of Array .from (this [_inputRoutes] ?? X3DField_EMPTY))
         route .dispose ();

      for (const route of Array .from (this [_outputRoutes] ?? X3DField_EMPTY))
         route .dispose ();

      this [_references]          ?.clear ();
      this [_referencesCallbacks] ?.clear ();
      this [_fieldInterests]      ?.clear ();
      this [_fieldCallbacks]      ?.clear ();
      this [_inputRoutes]         ?.clear ();
      this [_outputRoutes]        ?.clear ();
      this [_routeCallbacks]      ?.clear ();

      Base_X3DChildObject .prototype .dispose .call (this);
   }
});

for (const key of Object .keys (X3DField .prototype))
   Object .defineProperty (X3DField .prototype, key, { enumerable: false });

Object .defineProperties (X3DField,
{
   addStaticProperties:
   {
      value (constructor, typeName)
      {
         Object .defineProperties (constructor,
         {
            type:
            {
               value: Base_X3DConstants [typeName],
               enumerable: true,
            },
            typeName:
            {
               value: typeName,
               enumerable: true,
            },
            fromValue:
            {
               value (value)
               {
                  const field = Object .create (this .prototype);

                  X3DField .call (field, value);

                  return field;
               },
            },
            fromString:
            {
               value (value, scene)
               {
                  const field = new this ();

                  field .fromString (value, scene);

                  return field;
               },
            },
            fromVRMLString:
            {
               value (value, scene)
               {
                  const field = new this ();

                  field .fromVRMLString (value, scene);

                  return field;
               },
            },
            fromXMLString:
            {
               value (value, scene)
               {
                  const field = new this ();

                  field .fromXMLString (value, scene);

                  return field;
               },
            },
            fromJSONString:
            {
               value (value, scene)
               {
                  const field = new this ();

                  field .fromJSONString (value, scene);

                  return field;
               },
            },
         });
      },
   },
})

const X3DField_default_ = X3DField;
;

/* harmony default export */ const Base_X3DField = (Namespace/* default */.A .add ("X3DField", X3DField_default_));
;// ./src/x_ite/Base/FieldArray.js



function FieldArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .getName (), value]), Base_X3DField);
}

Object .setPrototypeOf (FieldArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (FieldArray .prototype))
   Object .defineProperty (FieldArray .prototype, key, { enumerable: false });

Object .defineProperties (FieldArray,
{
   typeName:
   {
      value: "FieldArray",
      enumerable: true,
   },
});

const FieldArray_default_ = FieldArray;
;

/* harmony default export */ const Base_FieldArray = (Namespace/* default */.A .add ("FieldArray", FieldArray_default_));
;// ./src/x_ite/Fields/SFScalar.js


function SFBoolTemplate (TypeName)
{
   function SFBool (value = false)
   {
      Base_X3DField .call (this, !! value);
   }

   return SFScalarPrototypeTemplate (SFBool, TypeName,
   {
      isDefaultValue ()
      {
         return this .getValue () === false;
      },
      set (value)
      {
         Base_X3DField .prototype .set .call (this, !! value);
      },
      toStream (generator)
      {
         generator .string += this .getValue () ? "TRUE" : "FALSE";
      },
      toXMLStream (generator)
      {
         generator .string += this .getValue () ? "true" : "false";
      },
      toJSONStreamValue (generator)
      {
         generator .string += this .getValue () ? "true" : "false";
      },
   });
}

function SFNumberTemplate (TypeName, double, defaultValue)
{
   const _formatter = double ? "DoubleFormat" : "FloatFormat";

   function SFNumber (value = defaultValue)
   {
      Base_X3DField .call (this, +value);
   }

   return SFScalarPrototypeTemplate (SFNumber, TypeName,
   {
      isDefaultValue ()
      {
         return this .getValue () === defaultValue;
      },
      set (value)
      {
         Base_X3DField .prototype .set .call (this, +value);
      },
      toStream (generator)
      {
         const category = this .getUnit ();

         generator .string += generator [_formatter] (generator .ToUnit (category, this .getValue ()));
      },
      toJSONStreamValue (generator)
      {
         const category = this .getUnit ();

         generator .string += generator .Number (generator [_formatter] (generator .ToUnit (category, this .getValue ())));
      },
   });
}

function SFInt32Template (TypeName)
{
   function SFInt32 (value = 0)
   {
      Base_X3DField .call (this, value|0);
   }

   return SFScalarPrototypeTemplate (SFInt32, TypeName,
   {
      isDefaultValue ()
      {
         return this .getValue () === 0;
      },
      set (value)
      {
         Base_X3DField .prototype .set .call (this, value|0);
      },
   });
}

function SFStringTemplate (TypeName)
{
   function SFString (value = "")
   {
      Base_X3DField .call (this, String (value));
   }

   SFScalarPrototypeTemplate (SFString, TypeName,
   {
      *[Symbol .iterator] ()
      {
         yield* this .getValue ();
      },
      isDefaultValue ()
      {
         return this .getValue () === "";
      },
      set (value)
      {
         Base_X3DField .prototype .set .call (this, String (value));
      },
      toStream (generator)
      {
         generator .string += '"';
         generator .string += SFString .escape (this .getValue ());
         generator .string += '"';
      },
      toXMLStream (generator, sourceText = false)
      {
         generator .string += sourceText
            ? generator .EncodeSourceText (this .getValue ())
            : generator .EncodeString (this .getValue ());
      },
      toJSONStreamValue (generator)
      {
         generator .string += '"';
         generator .string += generator .EncodeString (this .getValue ());
         generator .string += '"';
      },
   });

   Object .defineProperty (SFString .prototype, "length",
   {
      get ()
      {
         return this .getValue () .length;
      },
   });

   Object .assign (SFString,
   {
      unescape (string)
      {
         return string .replace (/\\([\\"])/g, "$1");
      },
      escape (string)
      {
         return string .replace (/([\\"])/g, "\\$1");
      },
   });

   return SFString;
}

function SFScalarPrototypeTemplate (Constructor, TypeName, properties = { })
{
   Base_X3DField .addStaticProperties (Constructor, TypeName);

   Object .assign (Object .setPrototypeOf (Constructor .prototype, Base_X3DField .prototype),
   {
      copy ()
      {
         return new Constructor (this .getValue ());
      },
      valueOf: Base_X3DField .prototype .getValue,
      toStream (generator)
      {
         generator .string += String (this .getValue ());
      },
      toVRMLStream (generator)
      {
         this .toStream (generator);
      },
      toXMLStream (generator)
      {
         this .toStream (generator);
      },
      toJSONStream (generator)
      {
         this .toJSONStreamValue (generator);
      },
      toJSONStreamValue (generator)
      {
         this .toStream (generator);
      },
   },
   properties);

   for (const key of Object .keys (Constructor .prototype))
      Object .defineProperty (Constructor .prototype, key, { enumerable: false });

   return Constructor;
}

const SFScalar = {
   SFBool:   SFBoolTemplate   ("SFBool"),
   SFDouble: SFNumberTemplate ("SFDouble", true,  0),
   SFFloat:  SFNumberTemplate ("SFFloat",  false, 0),
   SFInt32:  SFInt32Template  ("SFInt32"),
   SFString: SFStringTemplate ("SFString"),
   SFTime:   SFNumberTemplate ("SFTime",   true, -1),
};

const SFScalar_default_ = SFScalar;
;

/* harmony default export */ const Fields_SFScalar = (Namespace/* default */.A .add ("SFScalar", SFScalar_default_));
;// ./src/standard/Math/Numbers/Color3.js


const { interval, degrees, clamp } = Math_Algorithm;

const
   _r = Symbol .for ("X_ITE.Color3.r"),
   _g = Symbol .for ("X_ITE.Color3.g"),
   _b = Symbol .for ("X_ITE.Color3.b");

// glTF sometimes allows color values greater than 1.
// See: https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular/README.md
// See: https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/master/Models/SpecularTest/glTF/SpecularTest.gltf

function Color3 (r = 0, g = r, b = g)
{
   this [_r] = clamp (r, 0, 1);
   this [_g] = clamp (g, 0, 1);
   this [_b] = clamp (b, 0, 1);
}

Object .assign (Color3 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this [_r];
      yield this [_g];
      yield this [_b];
   },
   copy ()
   {
      const copy = Object .create (Color3 .prototype);
      copy [_r] = this [_r];
      copy [_g] = this [_g];
      copy [_b] = this [_b];
      return copy;
   },
   assign (color)
   {
      this [_r] = color [_r];
      this [_g] = color [_g];
      this [_b] = color [_b];
      return this;
   },
   set (r = 0, g = r, b = g)
   {
      this [_r] = clamp (r, 0, 1);
      this [_g] = clamp (g, 0, 1);
      this [_b] = clamp (b, 0, 1);
      return this;
   },
   equals (color)
   {
      return this [_r] === color [_r] &&
             this [_g] === color [_g] &&
             this [_b] === color [_b];
   },
   getHSV (result = [ ])
   {
      let h, s, v;

      const
         r = this [_r],
         g = this [_g],
         b = this [_b];

      const
         min = Math .min (r, g, b),
         max = Math .max (r, g, b);

      v = max; // value

      const delta = max - min;

      if (max !== 0 && delta !== 0)
      {
         s = delta / max; // s

         if (r === max)
            h =     (g - b) / delta;  // between yellow & magenta
         else if (g === max)
            h = 2 + (b - r) / delta;  // between cyan & yellow
         else
            h = 4 + (r - g) / delta;  // between magenta & cyan

         h *= Math .PI / 3;  // radiants

         if (h < 0)
            h += Math .PI * 2;
      }
      else
      {
         s = h = 0; // s = 0, h is undefined
      }

      result [0] = h;
      result [1] = s;
      result [2] = v;

      return result;
   },
   setHSV (h, s, v)
   {
      s = Math .max (s, 0),
      v = Math .max (v, 0);

      // H is given on [0, 2 * Pi]. S and V are given on [0, 1].
      // RGB are each returned on [0, 1].

      if (s === 0)
      {
         // achromatic (grey)
         this [_r] = this [_g] = this [_b] = v;
      }
      else
      {
         const w = degrees (interval (h, 0, Math .PI * 2)) / 60;     // sector 0 to 5

         const i = Math .floor (w);
         const f = w - i;                      // factorial part of h
         const p = v * ( 1 - s );
         const q = v * ( 1 - s * f );
         const t = v * ( 1 - s * ( 1 - f ) );

         switch (i % 6)
         {
            case 0:  this [_r] = v; this [_g] = t; this [_b] = p; break;
            case 1:  this [_r] = q; this [_g] = v; this [_b] = p; break;
            case 2:  this [_r] = p; this [_g] = v; this [_b] = t; break;
            case 3:  this [_r] = p; this [_g] = q; this [_b] = v; break;
            case 4:  this [_r] = t; this [_g] = p; this [_b] = v; break;
            default: this [_r] = v; this [_g] = p; this [_b] = q; break;
         }
      }

      return this;
   },
   linearToSRGB (color = new Color3 ())
   {
      color [_r] = Math .pow (this [_r], 1 / 2.2);
      color [_g] = Math .pow (this [_g], 1 / 2.2);
      color [_b] = Math .pow (this [_b], 1 / 2.2);

      return color;
   },
   sRGBToLinear (color = new Color3 ())
   {
      color [_r] = Math .pow (this [_r], 2.2);
      color [_g] = Math .pow (this [_g], 2.2);
      color [_b] = Math .pow (this [_b], 2.2);

      return color;
   },
   toString ()
   {
      return this [_r] + " " +
             this [_g] + " " +
             this [_b];
   },
});

for (const key of Object .keys (Color3 .prototype))
   Object .defineProperty (Color3 .prototype, key, { enumerable: false });

const r = {
   get () { return this [_r]; },
   set (value) { this [_r] = clamp (value, 0, 1); },
};

const g = {
   get () { return this [_g]; },
   set (value) { this [_g] = clamp (value, 0, 1); },
};

const b = {
   get () { return this [_b]; },
   set (value) { this [_b] = clamp (value, 0, 1); },
};

Object .defineProperties (Color3 .prototype,
{
   length: { value: 3 },
   0: r,
   1: g,
   2: b,
   r: Object .assign ({ enumerable: true }, r),
   g: Object .assign ({ enumerable: true }, g),
   b: Object .assign ({ enumerable: true }, b),
});

Object .assign (Color3,
{
   BLACK: Object .freeze (new Color3 ()),
   WHITE: Object .freeze (new Color3 (1)),
   HSV (h, s, v)
   {
      const color = Object .create (this .prototype);
      color .setHSV (h, s, v);
      return color;
   },
   lerp (a, b, t, r)
   {
      // Linearly interpolate in HSV space between source color @a a and destination color @a b by an amount of @a t.
      // Source and destination color must be in HSV space. The resulting HSV color is stored in @a r.

      let
         [ha, sa, va] = a,
         [hb, sb, vb] = b;

      if (sa === 0)
         ha = hb;

      if (sb === 0)
         hb = ha;

      const range = Math .abs (hb - ha);

      if (range <= Math .PI)
      {
         r [0] = ha + t * (hb - ha);
         r [1] = sa + t * (sb - sa);
         r [2] = va + t * (vb - va);
         return r;
      }

      const
         PI2  = Math .PI * 2,
         step = (PI2 - range) * t;

      let h = ha < hb ? ha - step : ha + step;

      if (h < 0)
         h += PI2;

      else if (h > PI2)
         h -= PI2;

      r [0] = h;
      r [1] = sa + t * (sb - sa);
      r [2] = va + t * (vb - va);
      return r;
   },
});

const Color3_default_ = Color3;
;

/* harmony default export */ const Numbers_Color3 = (Namespace/* default */.A .add ("Color3", Color3_default_));
;// ./src/x_ite/Fields/SFColor.js



function SFColor (r = 0, g = 0, b = 0)
{
   Base_X3DField .call (this, new Numbers_Color3 (+r, +g, +b));
}

Object .assign (Object .setPrototypeOf (SFColor .prototype, Base_X3DField .prototype),
{
   *[Symbol .iterator] ()
   {
      yield* this .getValue ();
   },
   copy ()
   {
      return SFColor .fromValue (this .getValue () .copy ());
   },
   equals (color)
   {
      return this .getValue () .equals (color .getValue ());
   },
   isDefaultValue ()
   {
      return this .getValue () .equals (Numbers_Color3 .BLACK);
   },
   set (value)
   {
      this .getValue () .assign (value);
   },
   getHSV ()
   {
      return this .getValue () .getHSV ([ ]);
   },
   setHSV (h, s, v)
   {
      this .getValue () .setHSV (+h, +s, +v);
      this .addEvent ();
   },
   linearToSRGB ()
   {
      return SFColor .fromValue (this .getValue () .linearToSRGB ());
   },
   sRGBToLinear ()
   {
      return SFColor .fromValue (this .getValue () .sRGBToLinear ());
   },
   lerp: (() =>
   {
      const
         s = [ ],
         d = [ ],
         r = [ ];

      return function (destination, t)
      {
         const result = new SFColor ();

         this .getValue () .getHSV (s);
         destination .getValue () .getHSV (d);

         Numbers_Color3 .lerp (s, d, +t, r);

         result .setHSV (r [0], r [1], r [2]);

         return result;
      };
   })(),
   toStream (generator)
   {
      const
         value = this .getValue (),
         last  = value .length - 1;

      for (let i = 0; i < last; ++ i)
      {
         generator .string += generator .FloatFormat (value [i]);
         generator .Space ();
      }

      generator .string += generator .FloatFormat (value [last]);
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      this .toStream (generator);
   },
   toJSONStream (generator)
   {
      generator .string += '[';
      generator .TidySpace ();

      this .toJSONStreamValue (generator);

      generator .TidySpace ();
      generator .string += ']';
   },
   toJSONStreamValue (generator)
   {
      const
         value = this .getValue (),
         last  = value .length - 1;

      for (let i = 0; i < last; ++ i)
      {
         generator .string += generator .Number (generator .FloatFormat (value [i]));
         generator .string += ',';
         generator .TidySpace ();
      }

      generator .string += generator .Number (generator .FloatFormat (value [last]));
   },
});

for (const key of Object .keys (SFColor .prototype))
   Object .defineProperty (SFColor .prototype, key, { enumerable: false });

const SFColor_r = {
   get ()
   {
      return this .getValue () .r;
   },
   set (value)
   {
      this .getValue () .r = +value;
      this .addEvent ();
   },
};

const SFColor_g = {
   get ()
   {
      return this .getValue () .g;
   },
   set (value)
   {
      this .getValue () .g = +value;
      this .addEvent ();
   },
};

const SFColor_b = {
   get ()
   {
      return this .getValue () .b;
   },
   set (value)
   {
      this .getValue () .b = +value;
      this .addEvent ();
   },
};

Object .defineProperties (SFColor .prototype,
{
   0: SFColor_r,
   1: SFColor_g,
   2: SFColor_b,
   r: Object .assign ({ enumerable: true }, SFColor_r),
   g: Object .assign ({ enumerable: true }, SFColor_g),
   b: Object .assign ({ enumerable: true }, SFColor_b),
});

Base_X3DField .addStaticProperties (SFColor, "SFColor");

Object .defineProperties (SFColor, Object .fromEntries ([
   "BLACK",
   "WHITE",
]
.map (key => [key, { value: SFColor .fromValue (Numbers_Color3 [key]), enumerable: true }])));

Object .defineProperties (SFColor,
{
   fromHSV:
   {
      value (h, s, v)
      {
         const color = new this ();

         color .setHSV (h, s, v);

         return color;
      },
   },
});

const SFColor_default_ = SFColor;
;

/* harmony default export */ const Fields_SFColor = (Namespace/* default */.A .add ("SFColor", SFColor_default_));
;// ./src/standard/Math/Numbers/Color4.js



const { clamp: Color4_clamp } = Math_Algorithm;

const
   Color4_r = Symbol .for ("X_ITE.Color3.r"),
   Color4_g = Symbol .for ("X_ITE.Color3.g"),
   Color4_b = Symbol .for ("X_ITE.Color3.b"),
   _a = Symbol ();

// glTF sometimes allows color values greater than 1.
// See: https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular/README.md
// See: https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/master/Models/SpecularTest/glTF/SpecularTest.gltf

function Color4 (r = 0, g = r, b = g, a = b)
{
   this [Color4_r] = Color4_clamp (r, 0, 1);
   this [Color4_g] = Color4_clamp (g, 0, 1);
   this [Color4_b] = Color4_clamp (b, 0, 1);
   this [_a] = Color4_clamp (a, 0, 1);
}

Object .assign (Color4 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this [Color4_r];
      yield this [Color4_g];
      yield this [Color4_b];
      yield this [_a];
   },
   copy ()
   {
      const copy = Object .create (Color4 .prototype);
      copy [Color4_r] = this [Color4_r];
      copy [Color4_g] = this [Color4_g];
      copy [Color4_b] = this [Color4_b];
      copy [_a] = this [_a];
      return copy;
   },
   assign (color)
   {
      this [Color4_r] = color [Color4_r];
      this [Color4_g] = color [Color4_g];
      this [Color4_b] = color [Color4_b];
      this [_a] = color [_a];
      return this;
   },
   set (r = 0, g = r, b = g, a = b)
   {
      this [Color4_r] = Color4_clamp (r, 0, 1);
      this [Color4_g] = Color4_clamp (g, 0, 1);
      this [Color4_b] = Color4_clamp (b, 0, 1);
      this [_a] = Color4_clamp (a, 0, 1);
      return this;
   },
   equals (color)
   {
      return this [Color4_r] === color [Color4_r] &&
             this [Color4_g] === color [Color4_g] &&
             this [Color4_b] === color [Color4_b] &&
             this [_a] === color [_a];
   },
   getHSVA (result)
   {
      Numbers_Color3 .prototype .getHSV .call (this, result);

      result [3] = this [_a];

      return result;
   },
   setHSVA (h, s, v, a)
   {
      Numbers_Color3 .prototype .setHSV .call (this, h, s, v);

      this [_a] = a;

      return this;
   },
   linearToSRGB (color = new Color4 ())
   {
      Numbers_Color3 .prototype .linearToSRGB .call (this, color);

      color [_a] = this [_a];

      return color;
   },
   sRGBToLinear (color = new Color4 ())
   {
      Numbers_Color3 .prototype .sRGBToLinear .call (this, color);

      color [_a] = this [_a];

      return color;
   },
   toString ()
   {
      return this [Color4_r] + " " +
             this [Color4_g] + " " +
             this [Color4_b] + " " +
             this [_a];
   },
});

for (const key of Object .keys (Color4 .prototype))
   Object .defineProperty (Color4 .prototype, key, { enumerable: false });

const Numbers_Color4_r = {
   get () { return this [Color4_r]; },
   set (value) { this [Color4_r] = Color4_clamp (value, 0, 1); },
};

const Numbers_Color4_g = {
   get () { return this [Color4_g]; },
   set (value) { this [Color4_g] = Color4_clamp (value, 0, 1); },
};

const Numbers_Color4_b = {
   get () { return this [Color4_b]; },
   set (value) { this [Color4_b] = Color4_clamp (value, 0, 1); },
};

const a = {
   get () { return this [_a]; },
   set (value) { this [_a] = Color4_clamp (value, 0, 1); },
};

Object .defineProperties (Color4 .prototype,
{
   length: { value: 4 },
   0: Numbers_Color4_r,
   1: Numbers_Color4_g,
   2: Numbers_Color4_b,
   3: a,
   r: Object .assign ({ enumerable: true }, Numbers_Color4_r),
   g: Object .assign ({ enumerable: true }, Numbers_Color4_g),
   b: Object .assign ({ enumerable: true }, Numbers_Color4_b),
   a: Object .assign ({ enumerable: true }, a),
});

Object .assign (Color4,
{
   TRANSPARENT: Object .freeze (new Color4 ()),
   BLACK: Object .freeze (new Color4 (0, 0, 0, 1)),
   WHITE: Object .freeze (new Color4 (1)),
   HSVA (h, s, v, a)
   {
      const color = Object .create (this .prototype);
      color .setHSVA (h, s, v, a);
      return color;
   },
   lerp (a, b, t, r)
   {
      // Linearely interpolate in HSVA space between source color @a a and destination color @a b by an amount of @a t.
      // Source and destination color must be in HSVA space. The resulting HSVA color is stored in @a r.
      const aa = a [3];
      Numbers_Color3 .lerp (a, b, t, r);
      r [3] = aa + t * (b [3] - aa);
      return r;
   },
});

const Color4_default_ = Color4;
;

/* harmony default export */ const Numbers_Color4 = (Namespace/* default */.A .add ("Color4", Color4_default_));
;// ./src/x_ite/Fields/SFColorRGBA.js




function SFColorRGBA (r = 0, g = 0, b = 0, a = 0)
{
   Base_X3DField .call (this, new Numbers_Color4 (+r, +g, +b, +a));
}

Object .assign (Object .setPrototypeOf (SFColorRGBA .prototype, Base_X3DField .prototype),
{
   *[Symbol .iterator] ()
   {
      yield* this .getValue ();
   },
   copy ()
   {
      return SFColorRGBA .fromValue (this .getValue () .copy ());
   },
   equals: Fields_SFColor .prototype .equals,
   isDefaultValue ()
   {
      return this .getValue () .equals (Numbers_Color4 .TRANSPARENT);
   },
   set: Fields_SFColor .prototype .set,
   getHSVA ()
   {
      return this .getValue () .getHSVA ([ ]);
   },
   setHSVA (h, s, v, a = 1)
   {
      this .getValue () .setHSVA (+h, +s, +v, +a);
      this .addEvent ();
   },
   linearToSRGB ()
   {
      return SFColorRGBA .fromValue (this .getValue () .linearToSRGB ());
   },
   sRGBToLinear ()
   {
      return SFColorRGBA .fromValue (this .getValue () .sRGBToLinear ());
   },
   lerp: (() =>
   {
      const
         s = [ ],
         d = [ ],
         r = [ ];

      return function (destination, t)
      {
         const result = new SFColorRGBA ();

         this .getValue () .getHSVA (s);
         destination .getValue () .getHSVA (d);

         Numbers_Color4 .lerp (s, d, +t, r);

         result .setHSVA (r [0], r [1], r [2], r [3]);

         return result;
      };
   })(),
   toStream: Fields_SFColor .prototype .toStream,
   toVRMLStream: Fields_SFColor .prototype .toVRMLStream,
   toXMLStream: Fields_SFColor .prototype .toXMLStream,
   toJSONStream: Fields_SFColor .prototype .toJSONStream,
   toJSONStreamValue: Fields_SFColor .prototype .toJSONStreamValue,
});

for (const key of Object .keys (SFColorRGBA .prototype))
   Object .defineProperty (SFColorRGBA .prototype, key, { enumerable: false });

const SFColorRGBA_r = {
   get ()
   {
      return this .getValue () .r;
   },
   set (value)
   {
      this .getValue () .r = +value;
      this .addEvent ();
   },
};

const SFColorRGBA_g = {
   get ()
   {
      return this .getValue () .g;
   },
   set (value)
   {
      this .getValue () .g = +value;
      this .addEvent ();
   },
};

const SFColorRGBA_b = {
   get ()
   {
      return this .getValue () .b;
   },
   set (value)
   {
      this .getValue () .b = +value;
      this .addEvent ();
   },
};

const SFColorRGBA_a = {
   get ()
   {
      return this .getValue () .a;
   },
   set (value)
   {
      this .getValue () .a = +value;
      this .addEvent ();
   },
};

Object .defineProperties (SFColorRGBA .prototype,
{
   0: SFColorRGBA_r,
   1: SFColorRGBA_g,
   2: SFColorRGBA_b,
   3: SFColorRGBA_a,
   r: Object .assign ({ enumerable: true }, SFColorRGBA_r),
   g: Object .assign ({ enumerable: true }, SFColorRGBA_g),
   b: Object .assign ({ enumerable: true }, SFColorRGBA_b),
   a: Object .assign ({ enumerable: true }, SFColorRGBA_a),
});

Base_X3DField .addStaticProperties (SFColorRGBA, "SFColorRGBA");

Object .defineProperties (SFColorRGBA, Object .fromEntries ([
   "BLACK",
   "TRANSPARENT",
   "WHITE",
]
.map (key => [key, { value: SFColorRGBA .fromValue (Numbers_Color4 [key]), enumerable: true }])));

Object .defineProperties (SFColorRGBA,
{
   fromHSVA:
   {
      value (h, s, v, a)
      {
         const color = new this ();

         color .setHSVA (h, s, v, a);

         return color;
      },
   },
});

const SFColorRGBA_default_ = SFColorRGBA;
;

/* harmony default export */ const Fields_SFColorRGBA = (Namespace/* default */.A .add ("SFColorRGBA", SFColorRGBA_default_));
;// ./src/x_ite/Fields/SFMatrixPrototypeTemplate.js


function SFMatrixPrototypeTemplate (Constructor, TypeName, Matrix, double, properties = { })
{
   const _formatter = double ? "DoubleFormat" : "FloatFormat";

   Base_X3DField .addStaticProperties (Constructor, TypeName);

   Object .assign (Object .setPrototypeOf (Constructor .prototype, Base_X3DField .prototype),
   {
      *[Symbol .iterator] ()
      {
         yield* this .getValue ();
      },
      copy ()
      {
         return Constructor .fromValue (this .getValue () .copy ());
      },
      equals (matrix)
      {
         return this .getValue () .equals (matrix .getValue ());
      },
      isDefaultValue ()
      {
         return this .getValue () .equals (Matrix .IDENTITY);
      },
      set (value)
      {
         this .getValue () .assign (value);
      },
      setTransform (translation, rotation, scale, scaleOrientation, center)
      {
         this .getValue () .setTransform (translation      ?.getValue (),
                                          rotation         ?.getValue (),
                                          scale            ?.getValue (),
                                          scaleOrientation ?.getValue (),
                                          center           ?.getValue ());
      },
      getTransform (translation, rotation, scale, scaleOrientation, center)
      {
         this .getValue () .getTransform (translation      ?.getValue (),
                                          rotation         ?.getValue (),
                                          scale            ?.getValue (),
                                          scaleOrientation ?.getValue (),
                                          center           ?.getValue ());

         translation      ?.addEvent ();
         rotation         ?.addEvent ();
         scale            ?.addEvent ();
         scaleOrientation ?.addEvent ();
      },
      determinant ()
      {
         return this .getValue () .determinant ();
      },
      transpose ()
      {
         return Constructor .fromValue (this .getValue () .copy () .transpose ());
      },
      inverse ()
      {
         return Constructor .fromValue (this .getValue () .copy () .inverse ());
      },
      multLeft (matrix)
      {
         return Constructor .fromValue (this .getValue () .copy () .multLeft (matrix .getValue ()));
      },
      multRight (matrix)
      {
         return Constructor .fromValue (this .getValue () .copy () .multRight (matrix .getValue ()));
      },
      multVecMatrix (vector)
      {
         return vector .constructor .fromValue (this .getValue () .multVecMatrix (vector .getValue () .copy ()));
      },
      multMatrixVec (vector)
      {
         return vector .constructor .fromValue (this .getValue () .multMatrixVec (vector .getValue () .copy ()));
      },
      multDirMatrix (vector)
      {
         return vector .constructor .fromValue (this .getValue () .multDirMatrix (vector .getValue () .copy ()));
      },
      multMatrixDir (vector)
      {
         return vector .constructor .fromValue (this .getValue () .multMatrixDir (vector .getValue () .copy ()));
      },
      translate (translation)
      {
         return Constructor .fromValue (this .getValue () .copy () .translate (translation .getValue ()));
      },
      rotate (rotation)
      {
         return Constructor .fromValue (this .getValue () .copy () .rotate (rotation .getValue ()));
      },
      scale (scale)
      {
         return Constructor .fromValue (this .getValue () .copy () .scale (scale .getValue ()));
      },
      toStream (generator)
      {
         const
            value = this .getValue (),
            last  = value .length - 1;

         for (let i = 0; i < last; ++ i)
         {
            generator .string += generator [_formatter] (value [i]);
            generator .Space ();
         }

         generator .string += generator [_formatter] (value [last]);
      },
      toVRMLStream (generator)
      {
         this .toStream (generator);
      },
      toXMLStream (generator)
      {
         this .toStream (generator);
      },
      toJSONStream (generator)
      {
         generator .string += '[';
         generator .TidySpace ();

         this .toJSONStreamValue (generator);

         generator .TidySpace ();
         generator .string += ']';
      },
      toJSONStreamValue (generator)
      {
         const
            value = this .getValue (),
            last  = value .length - 1;

         for (let i = 0; i < last; ++ i)
         {
            generator .string += generator .Number (generator [_formatter] (value [i]));
            generator .string += ',';
            generator .TidySpace ();
         }

         generator .string += generator .Number (generator [_formatter] (value [last]));
      },
   },
   properties);

   for (const key of Object .keys (Constructor .prototype))
      Object .defineProperty (Constructor .prototype, key, { enumerable: false });

   function defineProperty (i)
   {
      Object .defineProperty (Constructor .prototype, i,
      {
         get ()
         {
            return this .getValue () [i];
         },
         set (value)
         {
            this .getValue () [i] = +value;
            this .addEvent ();
         },
         enumerable: true,
      });
   }

   for (let i = 0; i < Matrix .prototype .length; ++ i)
      defineProperty (i);

   Object .defineProperties (Constructor, Object .fromEntries ([
      "IDENTITY",
      "ZERO",
   ]
   .map (key => [key, { value: Constructor .fromValue (Matrix [key]), enumerable: true }])));

   return Constructor;
}

const SFMatrixPrototypeTemplate_default_ = SFMatrixPrototypeTemplate;
;

/* harmony default export */ const Fields_SFMatrixPrototypeTemplate = (Namespace/* default */.A .add ("SFMatrixPrototypeTemplate", SFMatrixPrototypeTemplate_default_));
;// ./src/standard/Math/Numbers/Vector2.js


function Vector2 (x = 0, y = x)
{
   this .x = x;
   this .y = y;
}

Object .assign (Vector2 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this .x;
      yield this .y;
   },
   copy ()
   {
      const copy = Object .create (Vector2 .prototype);
      copy .x = this .x;
      copy .y = this .y;
      return copy;
   },
   assign ({ x, y })
   {
      this .x = x;
      this .y = y;
      return this;
   },
   set (x = 0, y = x)
   {
      this .x = x;
      this .y = y;
      return this;
   },
   equals ({ x, y })
   {
      return this .x === x &&
             this .y === y;
   },
   abs ()
   {
      const { x, y } = this;

      this .x = Math .abs (x);
      this .y = Math .abs (y);
      return this;
   },
   add ({ x, y })
   {
      this .x += x;
      this .y += y;
      return this;
   },
   clamp ({ x: minX, y: minY }, { x: maxX, y: maxY })
   {
      this .x = Math_Algorithm .clamp (this .x, minX, maxX);
      this .y = Math_Algorithm .clamp (this .y, minY, maxY);
      return this;
   },
   distance ({ x, y })
   {
      return Math .hypot (this .x - x,
                          this .y - y);
   },
   divide (value)
   {
      this .x /= value;
      this .y /= value;
      return this;
   },
   divVec ({ x, y })
   {
      this .x /= x;
      this .y /= y;
      return this;
   },
   dot ({ x, y })
   {
      return this .x * x +
             this .y * y;
   },
   inverse ()
   {
      this .x = 1 / this .x;
      this .y = 1 / this .y;
      return this;
   },
   lerp ({ x: dX, y: dY }, t)
   {
      const { x, y } = this;

      this .x = x + t * (dX - x);
      this .y = y + t * (dY - y);
      return this;
   },
   max (... args)
   {
      let { x, y } = this;

      for (const { x: argX, y: argY } of args)
      {
         x = Math .max (x, argX);
         y = Math .max (y, argY);
      }

      this .x = x;
      this .y = y;
      return this;
   },
   min (... args)
   {
      let { x, y } = this;

      for (const { x: argX, y: argY } of args)
      {
         x = Math .min (x, argX);
         y = Math .min (y, argY);
      }

      this .x = x;
      this .y = y;
      return this;
   },
   multiply (value)
   {
      this .x *= value;
      this .y *= value;
      return this;
   },
   multVec ({ x, y })
   {
      this .x *= x;
      this .y *= y;
      return this;
   },
   negate ()
   {
      this .x = -this .x;
      this .y = -this .y;
      return this;
   },
   norm ()
   {
      return Math .hypot (this .x, this .y);
   },
   normalize ()
   {
      const length = Math .hypot (this .x, this .y);

      if (length)
      {
         this .x /= length;
         this .y /= length;
      }

      return this;
   },
   reflect (normal)
   {
      const d = 2 * normal .dot (this);

      this .x -= normal .x * d;
      this .y -= normal .y * d;

      return this;
   },
   squaredNorm ()
   {
      const { x, y } = this;

      return x * x +
             y * y;
   },
   subtract ({ x, y })
   {
      this .x -= x;
      this .y -= y;
      return this;
   },
   toString ()
   {
      return this .x + " " +
             this .y;
   }
});

for (const key of Object .keys (Vector2 .prototype))
   Object .defineProperty (Vector2 .prototype, key, { enumerable: false });

Object .defineProperties (Vector2 .prototype,
{
   length: { value: 2 },
   0:
   {
      get () { return this .x; },
      set (value) { this .x = value; },
   },
   1:
   {
      get () { return this .y; },
      set (value) { this .y = value; },
   },
});

Object .assign (Vector2,
{
   ZERO: Object .freeze (new Vector2 ()),
   // Positive values
   ONE: Object .freeze (new Vector2 (1)),
   X_AXIS: Object .freeze (new Vector2 (1, 0)),
   Y_AXIS: Object .freeze (new Vector2 (0, 1)),
   // Negative values
   NEGATIVE_ONE: Object .freeze (new Vector2 (-1)),
   NEGATIVE_X_AXIS: Object .freeze (new Vector2 (-1, 0)),
   NEGATIVE_Y_AXIS: Object .freeze (new Vector2 (0, -1)),
});

const Vector2_default_ = Vector2;
;

/* harmony default export */ const Numbers_Vector2 = (Namespace/* default */.A .add ("Vector2", Vector2_default_));
;// ./src/standard/Math/Numbers/Vector3.js


function Vector3 (x = 0, y = x, z = y)
{
   this .x = x;
   this .y = y;
   this .z = z;
}

Object .assign (Vector3 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this .x;
      yield this .y;
      yield this .z;
   },
   copy ()
   {
      const copy = Object .create (Vector3 .prototype);
      copy .x = this .x;
      copy .y = this .y;
      copy .z = this .z;
      return copy;
   },
   assign ({ x, y, z })
   {
      this .x = x;
      this .y = y;
      this .z = z;
      return this;
   },
   set (x = 0, y = x, z = y)
   {
      this .x = x;
      this .y = y;
      this .z = z;
      return this;
   },
   equals ({ x, y, z })
   {
      return this .x === x &&
             this .y === y &&
             this .z === z;
   },
   abs ()
   {
      const { x, y, z } = this;

      this .x = Math .abs (x);
      this .y = Math .abs (y);
      this .z = Math .abs (z);
      return this;
   },
   add ({ x, y, z })
   {
      this .x += x;
      this .y += y;
      this .z += z;
      return this;
   },
   clamp ({ x: minX, y: minY, z: minZ }, { x: maxX, y: maxY, z: maxZ })
   {
      this .x = Math_Algorithm .clamp (this .x, minX, maxX);
      this .y = Math_Algorithm .clamp (this .y, minY, maxY);
      this .z = Math_Algorithm .clamp (this .z, minZ, maxZ);
      return this;
   },
   cross ({ x: bx, y: by, z: bz })
   {
      const { x: ax, y: ay, z: az } = this;

      this .x = ay * bz - az * by;
      this .y = az * bx - ax * bz;
      this .z = ax * by - ay * bx;

      return this;
   },
   distance ({ x, y, z })
   {
      return Math .hypot (this .x - x,
                          this .y - y,
                          this .z - z);
   },
   divide (value)
   {
      this .x /= value;
      this .y /= value;
      this .z /= value;
      return this;
   },
   divVec ({ x, y, z })
   {
      this .x /= x;
      this .y /= y;
      this .z /= z;
      return this;
   },
   dot ({ x, y, z })
   {
      return this .x * x +
             this .y * y +
             this .z * z;
   },
   inverse ()
   {
      this .x = 1 / this .x;
      this .y = 1 / this .y;
      this .z = 1 / this .z;
      return this;
   },
   lerp ({ x: dX, y: dY, z: dZ }, t)
   {
      const { x, y, z } = this;

      this .x = x + t * (dX - x);
      this .y = y + t * (dY - y);
      this .z = z + t * (dZ - z);
      return this;
   },
   max (... args)
   {
      let { x, y, z } = this;

      for (const { x: argX, y: argY, z: argZ } of args)
      {
         x = Math .max (x, argX);
         y = Math .max (y, argY);
         z = Math .max (z, argZ);
      }

      this .x = x;
      this .y = y;
      this .z = z;
      return this;
   },
   min (... args)
   {
      let { x, y, z } = this;

      for (const { x: argX, y: argY, z: argZ } of args)
      {
         x = Math .min (x, argX);
         y = Math .min (y, argY);
         z = Math .min (z, argZ);
      }

      this .x = x;
      this .y = y;
      this .z = z;
      return this;
   },
   multiply (value)
   {
      this .x *= value;
      this .y *= value;
      this .z *= value;
      return this;
   },
   multVec ({ x, y, z })
   {
      this .x *= x;
      this .y *= y;
      this .z *= z;
      return this;
   },
   negate ()
   {
      this .x = -this .x;
      this .y = -this .y;
      this .z = -this .z;
      return this;
   },
   norm ()
   {
      return Math .hypot (this .x, this .y, this .z);
   },
   normalize ()
   {
      const length = Math .hypot (this .x, this .y, this .z);

      if (length)
      {
         this .x /= length;
         this .y /= length;
         this .z /= length;
      }

      return this;
   },
   reflect (normal)
   {
      const d = 2 * normal .dot (this);

      this .x -= normal .x * d;
      this .y -= normal .y * d;
      this .z -= normal .z * d;

      return this;
   },
   slerp: (() =>
   {
      const tmp = new Vector3 ();

      return function (destination, t)
      {
         return Math_Algorithm .simpleSlerp (this, tmp .assign (destination), t);
      };
   })(),
   squaredNorm ()
   {
      const { x, y, z } = this;

      return x * x +
             y * y +
             z * z;
   },
   subtract ({ x, y, z })
   {
      this .x -= x;
      this .y -= y;
      this .z -= z;
      return this;
   },
   toString ()
   {
      return this .x + " " +
             this .y + " " +
             this .z;
   }
});

for (const key of Object .keys (Vector3 .prototype))
   Object .defineProperty (Vector3 .prototype, key, { enumerable: false });

Object .defineProperties (Vector3 .prototype,
{
   length: { value: 3 },
   0:
   {
      get () { return this .x; },
      set (value) { this .x = value; },
   },
   1:
   {
      get () { return this .y; },
      set (value) { this .y = value; },
   },
   2:
   {
      get () { return this .z; },
      set (value) { this .z = value; },
   },
});

Object .assign (Vector3,
{
   ZERO: Object .freeze (new Vector3 ()),
   // Positive values
   ONE: Object .freeze (new Vector3 (1)),
   X_AXIS: Object .freeze (new Vector3 (1, 0, 0)),
   Y_AXIS: Object .freeze (new Vector3 (0, 1, 0)),
   Z_AXIS: Object .freeze (new Vector3 (0, 0, 1)),
   // Negative values
   NEGATIVE_ONE: Object .freeze (new Vector3 (-1)),
   NEGATIVE_X_AXIS: Object .freeze (new Vector3 (-1, 0, 0)),
   NEGATIVE_Y_AXIS: Object .freeze (new Vector3 (0, -1, 0)),
   NEGATIVE_Z_AXIS: Object .freeze (new Vector3 (0, 0, -1)),
});

const Vector3_default_ = Vector3;
;

/* harmony default export */ const Numbers_Vector3 = (Namespace/* default */.A .add ("Vector3", Vector3_default_));
;// ./src/standard/Math/Numbers/Matrix2.js


function Matrix2 (m00 = 1, m01 = 0, m10 = 0, m11 = m00)
{
   this [0] = m00;
   this [1] = m01;
   this [2] = m10;
   this [3] = m11;
}

Object .assign (Matrix2 .prototype,
{
   *[Symbol .iterator] ()
   {
      for (let i = 0; i < 4; ++ i)
         yield this [i];
   },
   copy ()
   {
      const copy = Object .create (Matrix2 .prototype);

      for (let i = 0; i < 4; ++ i)
         copy [i] = this [i];

      return copy;
   },
   assign (matrix)
   {
      for (let i = 0; i < 4; ++ i)
         this [i] = matrix [i];

      return this;
   },
   equals (matrix)
   {
      for (let i = 0; i < 4; ++ i)
      {
         if (this [i] !== matrix [i])
            return false;
      }

      return true;
   },
   set1 (r, c, value)
   {
      this [r * this .order + c] = value;
   },
   get1 (r, c)
   {
      return this [r * this .order + c];
   },
   set (m00 = 1, m01 = 0, m10 = 0, m11 = m00)
   {
      this [0] = m00;
      this [1] = m01;
      this [2] = m10;
      this [3] = m11;
   },
   determinant1 ()
   {
      return this [0];
   },
   determinant ()
   {
      const { 0: m0, 1: m1, 2: m2, 3: m3 } = this;

      return m0 * m3 - m1 * m2;
   },
   transpose ()
   {
      const tmp = this [1];

      this [1] = this [2];
      this [2] = tmp;

      return this;
   },
   inverse ()
   {
      const { 0: A, 1: B, 2: C, 3: D } = this;

      // Calculate the determinant.
      let d = A * D - B * C;

      if (!d)
         return this .assign (Matrix2 .ZERO);

      d = 1 / d;

      this [0] =  D * d;
      this [1] = -B * d;
      this [2] = -C * d;
      this [3] =  A * d;

      return this;
   },
   multLeft (matrix)
   {
      const { 0: a0, 1: a1, 2: a2, 3: a3 } = this;

      var { 0: b0, 1: b1 } = matrix;

      this [0] = a0 * b0 + a2 * b1;
      this [1] = a1 * b0 + a3 * b1;

      var { 2: b0, 3: b1 } = matrix;

      this [2] = a0 * b0 + a2 * b1;
      this [3] = a1 * b0 + a3 * b1;

      return this;
   },
   multRight (matrix)
   {
      const { 0: b0, 1: b1, 2: b2, 3: b3 } = matrix;

      var { 0: a0, 1: a1 } = this;

      this [0] = b0 * a0 + b2 * a1;
      this [1] = b1 * a0 + b3 * a1;

      var { 2: a0, 3: a1 } = this;

      this [2] = b0 * a0 + b2 * a1;
      this [3] = b1 * a0 + b3 * a1;

      return this;
   },
   multVecMatrix (vector)
   {
      if (typeof vector === "number")
      {
         const
            x = vector,
            w = x * this [1] + this [3];

         return (x * this [0] + this [2]) / w;
      }
      else
      {
         const { x, y } = vector;

         vector .x = x * this [0] + y * this [2];
         vector .y = x * this [1] + y * this [3];

         return vector;
      }
   },
   multMatrixVec (vector)
   {
      if (typeof vector === "number")
      {
         const
            x = vector,
            w = x * this [2] + this [3];

         return (x * this [0] + this [1]) / w;
      }
      else
      {
         const { x, y } = vector;

         vector .x = x * this [0] + y * this [1];
         vector .y = x * this [2] + y * this [3];

         return vector;
      }
   },
   toString ()
   {
      return Array .prototype .join .call (this, " ");
   },
});

for (const key of Object .keys (Matrix2 .prototype))
   Object .defineProperty (Matrix2 .prototype, key, { enumerable: false });

Object .defineProperties (Matrix2 .prototype,
{
   order: { value: 2 },
   length: { value: 4 },
   x:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector2 ();

         return function () { return vector .set (this [0], this [1]); };
      })(),
   },
   y:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector2 ();

         return function () { return vector .set (this [2], this [3]); };
      })(),
   },
   xAxis:
   {
      get () { return this [0]; },
   },
   origin:
   {
      get () { return this [2]; },
   },
   submatrix:
   {
      get () { return this [0]; },
   },
});

Object .assign (Matrix2,
{
   ZERO: Object .freeze (new Matrix2 (0)),
   IDENTITY: Object .freeze (new Matrix2 ()),
});

const Matrix2_default_ = Matrix2;
;

/* harmony default export */ const Numbers_Matrix2 = (Namespace/* default */.A .add ("Matrix2", Matrix2_default_));
;// ./src/standard/Math/Algorithms/eigen_decomposition.js
const eigen_decomposition_a = [ ]; // more scratch
const eigen_decomposition_b = [ ]; // more scratch
const z = [ ]; // more scratch

// Calculate eigenvalues and eigenvectors.
// This is from SGI Inventor Matrix.cpp.
function eigen_decomposition (matrix, result)
{
   const
      ORDER   = matrix .order,
      SIZE    = matrix .length,
      values  = result .values,
      vectors = result .vectors;

   let
      sm,         // smallest entry
      theta,      // angle for Jacobi rotation
      c, s, t,    // cosine, sine, tangent of theta
      tau,        // sine / (1 + cos)
      h, g,       // two scrap values
      thresh,     // threshold below which no rotation done
      p, q, i, j; // indices

   // initializations
   for (i = 0; i < ORDER; ++ i)
   {
      eigen_decomposition_a [i] = eigen_decomposition_a [i] ?? [ ];
      eigen_decomposition_b [i] = values [i] = matrix .get1 (i, i);
      z [i] = 0;

      for (j = 0; j < ORDER; ++ j)
      {
         vectors [i] [j] = (i === j) ? 1 : 0;
         eigen_decomposition_a [i] [j] = matrix .get1 (j, i);
      }
   }

   // Why 50? I don't know--it's the way the folks who wrote the
   // algorithm did it:
   for (i = 0; i < 50; ++ i)
   {
      sm = 0;

      for (p = 0; p < ORDER - 1; ++ p)
      {
         for (q = p + 1; q < ORDER; ++ q)
            sm += Math .abs (eigen_decomposition_a [p] [q]);
      }

      if (sm === 0)
         break;

      thresh = i < 3 ?
         0.2 * sm / SIZE :
         0;

      for (p = 0; p < ORDER - 1; ++ p)
      {
         for (q = p + 1; q < ORDER; ++ q)
         {
            g = 100 * Math .abs (eigen_decomposition_a [p] [q]);

            if (i > 3
                && (Math .abs (values [p]) + g === Math .abs (values [p]))
                && (Math .abs (values [q]) + g === Math .abs (values [q]))
            )
            {
               eigen_decomposition_a [p] [q] = 0;
            }

            else if (Math .abs (eigen_decomposition_a [p] [q]) > thresh)
            {
               h = values [q] - values [p];

               if (Math .abs (h) + g === Math .abs (h))
               {
                  t = eigen_decomposition_a [p] [q] / h;
               }
               else
               {
                  theta = 0.5 * h / eigen_decomposition_a [p] [q];
                  t     = 1 / (Math .abs (theta) + Math .sqrt (1 + theta * theta));

                  if (theta < 0)  t = -t;
               }
               // End of computing tangent of rotation angle

               c           = 1 / Math .sqrt (1 + t * t);
               s           = t * c;
               tau         = s / (1 + c);
               h           = t * eigen_decomposition_a [p] [q];
               z [p]      -= h;
               z [q]      += h;
               values [p] -= h;
               values [q] += h;
               eigen_decomposition_a [p] [q]   = 0;

               for (j = 0; j < p; ++ j)
               {
                  g = eigen_decomposition_a [j] [p];
                  h = eigen_decomposition_a [j] [q];
                  eigen_decomposition_a [j] [p] = g - s * (h + g * tau);
                  eigen_decomposition_a [j] [q] = h + s * (g - h * tau);
               }

               for (j = p + 1; j < q; ++ j)
               {
                  g = eigen_decomposition_a [p] [j];
                  h = eigen_decomposition_a [j] [q];
                  eigen_decomposition_a [p] [j] = g - s * (h + g * tau);
                  eigen_decomposition_a [j] [q] = h + s * (g - h * tau);
               }

               for (j = q + 1; j < ORDER; ++ j)
               {
                  g = eigen_decomposition_a [p] [j];
                  h = eigen_decomposition_a [q] [j];
                  eigen_decomposition_a [p] [j] = g - s * (h + g * tau);
                  eigen_decomposition_a [q] [j] = h + s * (g - h * tau);
               }

               for (j = 0; j < ORDER; ++ j)
               {
                  g = vectors [j] [p];
                  h = vectors [j] [q];
                  vectors [j] [p] = g - s * (h + g * tau);
                  vectors [j] [q] = h + s * (g - h * tau);
               }
            }
         }
      }

      for (p = 0; p < ORDER; ++ p)
      {
         values [p] = eigen_decomposition_b [p] += z [p];
         z [p] = 0;
      }
   }

   return result;
}

const eigen_decomposition_default_ = eigen_decomposition;
;

/* harmony default export */ const Algorithms_eigen_decomposition = (Namespace/* default */.A .add ("eigen_decomposition", eigen_decomposition_default_));
;// ./src/standard/Math/Numbers/Matrix3.js





function Matrix3 (m00 = 1, m01 = 0,   m02 = 0,
                  m10 = 0, m11 = m00, m12 = 0,
                  m20 = 0, m21 = 0,   m22 = m11)
{
   this [0] = m00;
   this [1] = m01;
   this [2] = m02;
   this [3] = m10;
   this [4] = m11;
   this [5] = m12;
   this [6] = m20;
   this [7] = m21;
   this [8] = m22;
}

Object .assign (Matrix3 .prototype,
{
   *[Symbol .iterator] ()
   {
      for (let i = 0; i < 9; ++ i)
         yield this [i];
   },
   copy ()
   {
      const copy = Object .create (Matrix3 .prototype);

      for (let i = 0; i < 9; ++ i)
         copy [i] = this [i];

      return copy;
   },
   assign (matrix)
   {
      for (let i = 0; i < 9; ++ i)
         this [i] = matrix [i];

      return this;
   },
   equals (matrix)
   {
      for (let i = 0; i < 9; ++ i)
      {
         if (this [i] !== matrix [i])
            return false;
      }

      return true;
   },
   set1 (r, c, value)
   {
      this [r * this .order + c] = value;

      return this;
   },
   get1 (r, c)
   {
      return this [r * this .order + c];
   },
   set (m00 = 1, m01 = 0,   m02 = 0,
        m10 = 0, m11 = m00, m12 = 0,
        m20 = 0, m21 = 0,   m22 = m11)
   {
      this [0] = m00;
      this [1] = m01;
      this [2] = m02;
      this [3] = m10;
      this [4] = m11;
      this [5] = m12;
      this [6] = m20;
      this [7] = m21;
      this [8] = m22;

      return this;
   },
   getTransform: (() =>
   {
      const c = new Numbers_Vector2 ();

      return function (translation, rotation, scale, scaleOrientation, center)
      {
         if (center)
         {
            m .setTransform (c .assign (center) .negate ());
            m .multLeft (this);
            m .translate (center);
            m .getTransform (translation, rotation, scale, scaleOrientation);
         }
         else
         {
            this .factor (translation, rotation, scale, scaleOrientation);
         }

         return this;
      };
   })(),
   setTransform: (() =>
   {
      const invCenter = new Numbers_Vector2 ();

      return function (translation, rotation, scale, scaleOrientation, center)
      {
         this .set ();

         // P' = T * C * R * SR * S * -SR * -C * P
         if (translation ?.equals (Numbers_Vector2 .ZERO) === false)
            this .translate (translation);

         const hasCenter = center ?.equals (Numbers_Vector2 .ZERO) === false;

         if (hasCenter)
            this .translate (center);

         if (rotation)
            this .rotate (rotation);

         if (scale ?.equals (Numbers_Vector2 .ONE) === false)
         {
            if (scaleOrientation)
            {
               this .rotate (scaleOrientation);
               this .scale (scale);
               this .rotate (-scaleOrientation);
            }
            else
            {
               this .scale (scale);
            }
         }

         if (hasCenter)
            this .translate (invCenter .assign (center) .negate ());

         return this;
      };
   })(),
   factor: (() =>
   {
      const
         s  = new Numbers_Vector2 (),
         si = new Numbers_Matrix2 (),
         so = new Numbers_Matrix2 (),
         b  = new Numbers_Matrix2 ();

      const eigen = { values: [ ], vectors: [[ ], [ ]] };

      return function (translation, rotation, scale, scaleOrientation)
      {
         // (1) Get translation.
         translation ?.set (this [6], this [7]);

         if (!(rotation || scale || scaleOrientation))
            return;

         // (2) Create 3x3 matrix.
         const a = this .submatrix;

         // (3) Compute det A. If negative, set sign = -1, else sign = 1
         const det      = a .determinant ();
         const det_sign = det < 0 ? -1 : 1;

         // (4) B = A * !A  (here !A means A transpose)
         b .assign (a) .transpose () .multLeft (a);
         const e = Algorithms_eigen_decomposition (b, eigen);

         // Find min / max eigenvalues and do ratio test to determine singularity.

         so .set (e .vectors [0] [0], e .vectors [1] [0],
                  e .vectors [0] [1], e .vectors [1] [1]);

         if (scaleOrientation)
         {
            scaleOrientation [0] = so [0];
            scaleOrientation [1] = so [1];
            scaleOrientation [2] = Math .atan2 (so [1], so [0]);
         }

         // Compute s = sqrt(eigen values), with sign. Set si = s-inverse

         s .set (det_sign * Math .sqrt (e .values [0]),
                 det_sign * Math .sqrt (e .values [1]));

         scale ?.assign (s);

         if (rotation)
         {
            si [0] = 1 / s .x;
            si [3] = 1 / s .y;

            // (5) Compute U = !R ~S R A.
            a .multLeft (so) .multLeft (si) .multLeft (so .transpose ());

            rotation [0] = a [0];
            rotation [1] = a [1];
            rotation [2] = Math .atan2 (a [1], a [0]);
         }
      };
   })(),
   determinant2 ()
   {
      const { 0: m0, 1: m1, 3: m3, 4: m4 } = this;

      return m0 * m4 - m1 * m3;
   },
   determinant ()
   {
      const { 0: m0, 1: m1, 2: m2, 3: m3, 4: m4, 5: m5, 6: m6, 7: m7, 8: m8 } = this;

      return m0 * (m4 * m8 - m5 * m7) -
             m1 * (m3 * m8 - m5 * m6) +
             m2 * (m3 * m7 - m4 * m6);
   },
   transpose ()
   {
      let tmp;

      tmp = this [1]; this [1] = this [3]; this [3] = tmp;
      tmp = this [2]; this [2] = this [6]; this [6] = tmp;
      tmp = this [5]; this [5] = this [7]; this [7] = tmp;

      return this;
   },
   inverse ()
   {
      // Complexity 2 +, 16 -, 33 *, 1 /.

      const
         { 0: m0, 1: m1, 2: m2, 3: m3, 4: m4, 5: m5, 6: m6, 7: m7, 8: m8 } = this,
         t4  = m0 * m4,
         t6  = m0 * m7,
         t8  = m3 * m1,
         t10 = m3 * m7,
         t12 = m6 * m1,
         t14 = m6 * m4;

      // Calculate the determinant.
      let d = (t4 * m8 - t6 * m5 - t8 * m8 + t10 * m2 + t12 * m5 - t14 * m2);

      if (!d)
         return this .assign (Matrix3 .ZERO);

      d = 1 / d;

      const
         b0 =  (m4 * m8 - m7 * m5) * d,
         b1 = -(m1 * m8 - m7 * m2) * d,
         b2 =  (m1 * m5 - m4 * m2) * d,
         b3 = -(m3 * m8 - m6 * m5) * d,
         b4 =  (m0 * m8 - m6 * m2) * d,
         b5 = -(m0 * m5 - m3 * m2) * d;

      this [0] = b0;
      this [1] = b1;
      this [2] = b2;
      this [3] = b3;
      this [4] = b4;
      this [5] = b5;
      this [6] =  (t10 - t14) * d;
      this [7] = -(t6 - t12) * d;
      this [8] =  (t4 - t8) * d;

      return this;
   },
   multLeft (matrix)
   {
      // Complexity 18 +, 27 *.

      const { 0: a0, 1: a1, 2: a2, 3: a3, 4: a4, 5: a5, 6: a6, 7: a7, 8: a8 } = this;

      var { 0: b0, 1: b1, 2: b2 } = matrix;

      this [0] = a0 * b0 + a3 * b1 + a6 * b2;
      this [1] = a1 * b0 + a4 * b1 + a7 * b2;
      this [2] = a2 * b0 + a5 * b1 + a8 * b2;

      var { 3: b0, 4: b1, 5: b2 } = matrix;

      this [3] = a0 * b0 + a3 * b1 + a6 * b2;
      this [4] = a1 * b0 + a4 * b1 + a7 * b2;
      this [5] = a2 * b0 + a5 * b1 + a8 * b2;

      var { 6: b0, 7: b1, 8: b2 } = matrix;

      this [6] = a0 * b0 + a3 * b1 + a6 * b2;
      this [7] = a1 * b0 + a4 * b1 + a7 * b2;
      this [8] = a2 * b0 + a5 * b1 + a8 * b2;

      return this;
   },
   multRight (matrix)
   {
      // Complexity 18 +, 27 *.

      const { 0: b0, 1: b1, 2: b2, 3: b3, 4: b4, 5: b5, 6: b6, 7: b7, 8: b8 } = matrix;

      var { 0: a0, 1: a1, 2: a2 } = this;

      this [0] = a0 * b0 + a1 * b3 + a2 * b6;
      this [1] = a0 * b1 + a1 * b4 + a2 * b7;
      this [2] = a0 * b2 + a1 * b5 + a2 * b8;

      var { 3: a0, 4: a1, 5: a2 } = this;

      this [3] = a0 * b0 + a1 * b3 + a2 * b6;
      this [4] = a0 * b1 + a1 * b4 + a2 * b7;
      this [5] = a0 * b2 + a1 * b5 + a2 * b8;

      var { 6: a0, 7: a1, 8: a2 } = this;

      this [6] = a0 * b0 + a1 * b3 + a2 * b6;
      this [7] = a0 * b1 + a1 * b4 + a2 * b7;
      this [8] = a0 * b2 + a1 * b5 + a2 * b8;

      return this;
   },
   multVecMatrix (vector)
   {
      if (vector .length === 2)
      {
         const
            { x, y } = vector,
            w = x * this [2] + y * this [5] + this [8];

         vector .x = (x * this [0] + y * this [3] + this [6]) / w;
         vector .y = (x * this [1] + y * this [4] + this [7]) / w;

         return vector;
      }
      else
      {
         const { x, y, z } = vector;

         vector .x = x * this [0] + y * this [3] + z * this [6];
         vector .y = x * this [1] + y * this [4] + z * this [7];
         vector .z = x * this [2] + y * this [5] + z * this [8];

         return vector;
      }
   },
   multMatrixVec (vector)
   {
      if (vector .length === 2)
      {
         const
            { x, y } = vector,
            w = x * this [6] + y * this [7] + this [8];

         vector .x = (x * this [0] + y * this [1] + this [2]) / w;
         vector .y = (x * this [3] + y * this [4] + this [5]) / w;

         return vector;
      }
      else
      {
         const { x, y, z } = vector;

         vector .x = x * this [0] + y * this [1] + z * this [2];
         vector .y = x * this [3] + y * this [4] + z * this [5];
         vector .z = x * this [6] + y * this [7] + z * this [8];

         return vector;
      }
   },
   multDirMatrix (vector)
   {
      const { x, y } = vector;

      vector .x = x * this [0] + y * this [3];
      vector .y = x * this [1] + y * this [4];

      return vector;
   },
   multMatrixDir (vector)
   {
      const { x, y } = vector;

      vector .x = x * this [0] + y * this [1];
      vector .y = x * this [3] + y * this [4];

      return vector;
   },
   translate (translation)
   {
      const { x, y } = translation;

      this [6] += this [0] * x + this [3] * y;
      this [7] += this [1] * x + this [4] * y;

      return this;
   },
   rotate (rotation)
   {
      return this .multLeft (Matrix3 .fromRotation (rotation));
   },
   scale (scale)
   {
      const { x, y } = scale;

      this [0] *= x;
      this [3] *= y;

      this [1] *= x;
      this [4] *= y;

      return this;
   },
   skewX (angle)
   {
      const a = Math .tan (angle);

      this [3] += this [0] * a;
      this [4] += this [1] * a;
      this [5] += this [2] * a;

      return this;
   },
   skewY (angle)
   {
      const a = Math .tan (angle);

      this [0] += this [3] * a;
      this [1] += this [4] * a;
      this [2] += this [5] * a;

      return this;
   },
   toString ()
   {
      return Array .prototype .join .call (this, " ");
   },
});

for (const key of Object .keys (Matrix3 .prototype))
   Object .defineProperty (Matrix3 .prototype, key, { enumerable: false });

Object .defineProperties (Matrix3 .prototype,
{
   order: { value: 3 },
   length: { value: 9 },
   x:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [0], this [1], this [2]); };
      })(),
   },
   y:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [3], this [4], this [5]); };
      })(),
   },
   z:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [6], this [7], this [8]); };
      })(),
   },
   xAxis:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector2 ();

         return function () { return vector .set (this [0], this [1]); };
      })(),
   },
   yAxis:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector2 ();

         return function () { return vector .set (this [3], this [4]); };
      })(),
   },
   origin:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector2 ();

         return function () { return vector .set (this [6], this [7]); };
      })(),
   },
   submatrix:
   {
      get: (() =>
      {
         const matrix = new Numbers_Matrix2 ();

         return function ()
         {
            matrix [0] = this [0]; matrix [1] = this [1];
            matrix [2] = this [3]; matrix [3] = this [4];
            return matrix;
         };
      })(),
   },
});

Object .assign (Matrix3,
{
   ZERO: Object .freeze (new Matrix3 (0)),
   IDENTITY: Object .freeze (new Matrix3 ()),
   fromTransform (translation, rotation, scale, scaleOrientation, center)
   {
      return Object .create (this .prototype) .setTransform (translation, rotation, scale, scaleOrientation, center);
   },
   fromRotation (rotation)
   {
      const
         sinAngle = Math .sin (rotation),
         cosAngle = Math .cos (rotation);

      return new Matrix3 ( cosAngle, sinAngle, 0,
                          -sinAngle, cosAngle, 0,
                           0, 0, 1);
   },
   fromMatrix2 (matrix)
   {
      return new Matrix3 (matrix [0], matrix [1], 0,
                          matrix [2], matrix [3], 0,
                          0, 0, 1);
   },
});

const m = new Matrix3 ();

const Matrix3_default_ = Matrix3;
;

/* harmony default export */ const Numbers_Matrix3 = (Namespace/* default */.A .add ("Matrix3", Matrix3_default_));
;// ./src/x_ite/Fields/SFMatrix3.js





function SFMatrix3Template (TypeName, double)
{
   function SFMatrix3 (m00 = 1, m01 = 0, m02 = 0,
                       m10 = 0, m11 = 1, m12 = 0,
                       m20 = 0, m21 = 0, m22 = 1)
   {
      Base_X3DField .call (this, new Numbers_Matrix3 (+m00, +m01, +m02,
                                         +m10, +m11, +m12,
                                         +m20, +m21, +m22));
   }

   return Fields_SFMatrixPrototypeTemplate (SFMatrix3, TypeName, Numbers_Matrix3, double,
   {
      setTransform (translation, rotation, scale, scaleOrientation, center)
      {
         this .getValue () .setTransform (translation ?.getValue (),
                                          rotation,
                                          scale ?.getValue (),
                                          scaleOrientation,
                                          center ?.getValue ());
      },
      rotate (rotation)
      {
         return SFMatrix3 .fromValue (this .getValue () .copy () .rotate (+rotation));
      },
      skewX (angle)
      {
         return SFMatrix3 .fromValue (this .getValue () .copy () .skewX (+angle));
      },
      skewY (angle)
      {
         return SFMatrix3 .fromValue (this .getValue () .copy () .skewY (+angle));
      },
   });
}

const SFMatrix3 = {
   SFMatrix3d: SFMatrix3Template ("SFMatrix3d", true),
   SFMatrix3f: SFMatrix3Template ("SFMatrix3f", false),
};

const SFMatrix3_default_ = SFMatrix3;
;

/* harmony default export */ const Fields_SFMatrix3 = (Namespace/* default */.A .add ("SFMatrix3", SFMatrix3_default_));
;// ./src/standard/Math/Numbers/Vector4.js


function Vector4 (x = 0, y = x, z = y, w = z)
{
   this .x = x;
   this .y = y;
   this .z = z;
   this .w = w;
}

Object .assign (Vector4 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this .x;
      yield this .y;
      yield this .z;
      yield this .w;
   },
   copy ()
   {
      const copy = Object .create (Vector4 .prototype);
      copy .x = this .x;
      copy .y = this .y;
      copy .z = this .z;
      copy .w = this .w;
      return copy;
   },
   assign ({ x, y, z, w })
   {
      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   set (x = 0, y = x, z = y, w = z)
   {
      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   equals ({ x, y, z, w })
   {
      return this .x === x &&
             this .y === y &&
             this .z === z &&
             this .w === w;
   },
   abs ()
   {
      const { x, y, z, w } = this;

      this .x = Math .abs (x);
      this .y = Math .abs (y);
      this .z = Math .abs (z);
      this .w = Math .abs (w);
      return this;
   },
   add ({ x, y, z, w })
   {
      this .x += x;
      this .y += y;
      this .z += z;
      this .w += w;
      return this;
   },
   clamp  ({ x: minX, y: minY, z: minZ, w: minW }, { x: maxX, y: maxY, z: maxZ, w: maxW })
   {
      this .x = Math_Algorithm .clamp (this .x, minX, maxX);
      this .y = Math_Algorithm .clamp (this .y, minY, maxY);
      this .z = Math_Algorithm .clamp (this .z, minZ, maxZ);
      this .w = Math_Algorithm .clamp (this .w, minW, maxW);
      return this;
   },
   distance ({ x, y, z, w })
   {
      return Math .hypot (this .x - x,
                          this .y - y,
                          this .z - z,
                          this .w - w);
   },
   divide (value)
   {
      this .x /= value;
      this .y /= value;
      this .z /= value;
      this .w /= value;
      return this;
   },
   divVec ({ x, y, z, w })
   {
      this .x /= x;
      this .y /= y;
      this .z /= z;
      this .w /= w;
      return this;
   },
   dot ({ x, y, z, w })
   {
      return this .x * x +
             this .y * y +
             this .z * z +
             this .w * w;
   },
   inverse ()
   {
      this .x = 1 / this .x;
      this .y = 1 / this .y;
      this .z = 1 / this .z;
      this .w = 1 / this .w;
      return this;
   },
   lerp ({ x: dX, y: dY, z: dZ, w: dW }, t)
   {
      const { x, y, z, w } = this;

      this .x = x + t * (dX - x);
      this .y = y + t * (dY - y);
      this .z = z + t * (dZ - z);
      this .w = w + t * (dW - w);
      return this;
   },
   max (... args)
   {
      let { x, y, z, w } = this;

      for (const { x: argX, y: argY, z: argZ, w: argW } of args)
      {
         x = Math .max (x, argX);
         y = Math .max (y, argY);
         z = Math .max (z, argZ);
         w = Math .max (w, argW);
      }

      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   min (... args)
   {
      let { x, y, z, w } = this;

      for (const { x: argX, y: argY, z: argZ, w: argW } of args)
      {
         x = Math .min (x, argX);
         y = Math .min (y, argY);
         z = Math .min (z, argZ);
         w = Math .min (w, argW);
      }

      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   multiply (value)
   {
      this .x *= value;
      this .y *= value;
      this .z *= value;
      this .w *= value;
      return this;
   },
   multVec ({ x, y, z, w })
   {
      this .x *= x;
      this .y *= y;
      this .z *= z;
      this .w *= w;
      return this;
   },
   negate ()
   {
      this .x = -this .x;
      this .y = -this .y;
      this .z = -this .z;
      this .w = -this .w;
      return this;
   },
   norm ()
   {
      return Math .hypot (this .x, this .y, this .z, this .w);
   },
   normalize ()
   {
      const length = Math .hypot (this .x, this .y, this .z, this .w);

      if (length)
      {
         this .x /= length;
         this .y /= length;
         this .z /= length;
         this .w /= length;
      }

      return this;
   },
   reflect (normal)
   {
      const d = 2 * normal .dot (this);

      this .x -= normal .x * d;
      this .y -= normal .y * d;
      this .z -= normal .z * d;
      this .w -= normal .w * d;

      return this;
   },
   squaredNorm ()
   {
      const { x, y, z, w } = this;

      return x * x +
             y * y +
             z * z +
             w * w;
   },
   subtract ({ x, y, z, w })
   {
      this .x -= x;
      this .y -= y;
      this .z -= z;
      this .w -= w;
      return this;
   },
   toString ()
   {
      return this .x + " " +
             this .y + " " +
             this .z + " " +
             this .w;
   },
});

for (const key of Object .keys (Vector4 .prototype))
   Object .defineProperty (Vector4 .prototype, key, { enumerable: false });

Object .defineProperties (Vector4 .prototype,
{
   length: { value: 4 },
   0:
   {
      get () { return this .x; },
      set (value) { this .x = value; },
   },
   1:
   {
      get () { return this .y; },
      set (value) { this .y = value; },
   },
   2:
   {
      get () { return this .z; },
      set (value) { this .z = value; },
   },
   3:
   {
      get () { return this .w; },
      set (value) { this .w = value; },
   },
});

Object .assign (Vector4,
{
   ZERO: Object .freeze (new Vector4 ()),
   // Positive values
   ONE: Object .freeze (new Vector4 (1)),
   X_AXIS: Object .freeze (new Vector4 (1, 0, 0, 0)),
   Y_AXIS: Object .freeze (new Vector4 (0, 1, 0, 0)),
   Z_AXIS: Object .freeze (new Vector4 (0, 0, 1, 0)),
   W_AXIS: Object .freeze (new Vector4 (0, 0, 0, 1)),
   // Negative values
   NEGATIVE_ONE: Object .freeze (new Vector4 (-1)),
   NEGATIVE_X_AXIS: Object .freeze (new Vector4 (-1, 0, 0, 0)),
   NEGATIVE_Y_AXIS: Object .freeze (new Vector4 (0, -1, 0, 0)),
   NEGATIVE_Z_AXIS: Object .freeze (new Vector4 (0, 0, -1, 0)),
   NEGATIVE_W_AXIS: Object .freeze (new Vector4 (0, 0, 0, -1)),
});

const Vector4_default_ = Vector4;
;

/* harmony default export */ const Numbers_Vector4 = (Namespace/* default */.A .add ("Vector4", Vector4_default_));
;// ./src/standard/Math/Numbers/Quaternion.js




function Quaternion (x = 0, y = 0, z = 0, w = 1)
{
   this .x = x;
   this .y = y;
   this .z = z;
   this .w = w;
}

Object .assign (Quaternion .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this .x;
      yield this .y;
      yield this .z;
      yield this .w;
   },
   copy ()
   {
      const copy = Object .create (Quaternion .prototype);
      copy .x = this .x;
      copy .y = this .y;
      copy .z = this .z;
      copy .w = this .w;
      return copy;
   },
   assign ({ x, y, z, w })
   {
      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   set (x = 0, y = 0, z = 0, w = 1)
   {
      this .x = x;
      this .y = y;
      this .z = z;
      this .w = w;
      return this;
   },
   setMatrix (matrix)
   {
      // First, find largest diagonal in matrix:

      const i = matrix [0] > matrix [4]
         ? matrix [0] > matrix [8] ? 0 : 2
         : matrix [4] > matrix [8] ? 1 : 2;

      const scaleRow = matrix [0] + matrix [4] + matrix [8];

      if (scaleRow > matrix [i * 3 + i])
      {
         // Compute w first:
         this [3] = Math .sqrt (scaleRow + 1) / 2;

         // And compute other values:
         const d = 4 * this [3];
         this [0] = (matrix [5] - matrix [7]) / d;
         this [1] = (matrix [6] - matrix [2]) / d;
         this [2] = (matrix [1] - matrix [3]) / d;
      }
      else
      {
         // Compute x, y, or z first:
         const j = (i + 1) % 3;
         const k = (i + 2) % 3;

         // Compute first value:
         this [i] = Math .sqrt (matrix [i * 3 + i] - matrix [j * 3 + j] - matrix [k * 3 + k] + 1) / 2;

         // And the others:
         const d = 4 * this [i];
         this [j] = (matrix [i * 3 + j] + matrix [j * 3 + i]) / d;
         this [k] = (matrix [i * 3 + k] + matrix [k * 3 + i]) / d;
         this [3] = (matrix [j * 3 + k] - matrix [k * 3 + j]) / d;
      }

      return this;
   },
   getMatrix (matrix = new Numbers_Matrix3 ())
   {
      const { x, y, z, w } = this;

      const
         a = x * x,
         b = x * y,
         c = y * y,
         d = y * z,
         e = z * x,
         f = z * z,
         g = w * x,
         h = w * y,
         i = w * z;

      matrix [0] = 1 - 2 * (c + f);
      matrix [1] =     2 * (b + i);
      matrix [2] =     2 * (e - h);

      matrix [3] =     2 * (b - i);
      matrix [4] = 1 - 2 * (f + a);
      matrix [5] =     2 * (d + g);

      matrix [6] =     2 * (e + h);
      matrix [7] =     2 * (d - g);
      matrix [8] = 1 - 2 * (c + a);

      return matrix;
   },
   /**
    * Sets the Euler components.
    * @param {number} x - The angle of the x axis in radians.
    * @param {number} y - The angle of the y axis in radians.
    * @param {number} z - The angle of the z axis in radians.
    * @param {string} order - A string representing the order that the rotations are applied.
    * @returns {Quaternion} A reference to this quaternion.
    */
   setEuler (x, y, z, order = "XYZ")
   {
      // https://github.com/toji/gl-matrix/blob/accefb6ddf1897a0dc443bbc7664c90e67af6455/src/quat.js#L460

      x /= 2;
      y /= 2;
      z /= 2;

      const
         sx = Math .sin (x),
         sy = Math .sin (y),
         sz = Math .sin (z),
         cx = Math .cos (x),
         cy = Math .cos (y),
         cz = Math .cos (z);

      switch (order)
      {
         case "XYZ":
            this .x = sx * cy * cz + cx * sy * sz;
            this .y = cx * sy * cz - sx * cy * sz;
            this .z = cx * cy * sz + sx * sy * cz;
            this .w = cx * cy * cz - sx * sy * sz;
            break;

         case "ZYX":
            this .x = sx * cy * cz - cx * sy * sz;
            this .y = cx * sy * cz + sx * cy * sz;
            this .z = cx * cy * sz - sx * sy * cz;
            this .w = cx * cy * cz + sx * sy * sz;
            break;

         case "YXZ":
            this .x = sx * cy * cz + cx * sy * sz;
            this .y = cx * sy * cz - sx * cy * sz;
            this .z = cx * cy * sz - sx * sy * cz;
            this .w = cx * cy * cz + sx * sy * sz;
            break;

         case "ZXY":
            this .x = sx * cy * cz - cx * sy * sz;
            this .y = cx * sy * cz + sx * cy * sz;
            this .z = cx * cy * sz + sx * sy * cz;
            this .w = cx * cy * cz - sx * sy * sz;
            break;

         case "YZX":
            this .x = sx * cy * cz + cx * sy * sz;
            this .y = cx * sy * cz + sx * cy * sz;
            this .z = cx * cy * sz - sx * sy * cz;
            this .w = cx * cy * cz - sx * sy * sz;
            break;

         case "XZY":
            this .x = sx * cy * cz - cx * sy * sz;
            this .y = cx * sy * cz - sx * cy * sz;
            this .z = cx * cy * sz + sx * sy * cz;
            this .w = cx * cy * cz + sx * sy * sz;
            break;
      }

      return this;
   },
   /**
    * Gets the Euler components.
    * @param {number[]} euler - Array to be returned.
    * @param {string} order - A string representing the order that the rotations are applied.
    * @returns {number[]} The angles of the Euler rotations in radians.
    */
   getEuler (euler = [ ], order = "XYZ")
   {
      // https://github.com/mrdoob/three.js/blob/7a4f6b6637fbf10f1f36c9bb1f34b32452e516c6/src/math/Euler.js#L189

      const { 0: m0, 1: m1, 2: m2, 3: m3, 4: m4, 5: m5, 6: m6, 7: m7, 8: m8 } = this .getMatrix (Quaternion_m);

      switch (order)
      {
         case "XYZ":
         {
            euler [1] = Math .asin (Math_Algorithm .clamp (m6, -1, 1));

            if (Math .abs (m6) < 0.9999999)
            {
               euler [0] = Math .atan2 (-m7, m8);
               euler [2] = Math .atan2 (-m3, m0);
            }
            else
            {
               euler [0] = Math .atan2 (m5, m4);
               euler [2] = 0;
            }

            break;
         }
         case "ZYX":
         {
            euler [1] = Math .asin (-Math_Algorithm .clamp (m2, -1, 1));

            if (Math .abs (m2) < 0.9999999)
            {
               euler [0] = Math .atan2 (m5, m8);
               euler [2] = Math .atan2 (m1, m0);
            }
            else
            {
               euler [0] = 0;
               euler [2] = Math .atan2 (-m3, m4);
            }

            break;
         }
         case "YXZ":
         {
            euler [0] = Math .asin (-Math_Algorithm .clamp (m7, -1, 1));

            if (Math .abs (m7) < 0.9999999)
            {
               euler [1] = Math .atan2 (m6, m8);
               euler [2] = Math .atan2 (m1, m4);

            }
            else
            {
               euler [1] = Math .atan2 (-m2, m0);
               euler [2] = 0;
            }

            break;
         }
         case "ZXY":
         {
            euler [0] = Math .asin (Math_Algorithm .clamp (m5, -1, 1));

            if (Math .abs (m5) < 0.9999999)
            {
               euler [1] = Math .atan2 (-m2, m8);
               euler [2] = Math .atan2 (-m3, m4);
            }
            else
            {
               euler [1] = 0;
               euler [2] = Math .atan2 (m1, m0);
            }

            break;
         }
         case "YZX":
         {
            euler [2] = Math .asin (Math_Algorithm .clamp (m1, -1, 1));

            if (Math .abs (m1) < 0.9999999)
            {
               euler [0] = Math .atan2 (-m7, m4);
               euler [1] = Math .atan2 (-m2, m0);
            }
            else
            {
               euler [0] = 0;
               euler [1] = Math .atan2 (m6, m8);
            }

            break;
         }
         case "XZY":
         {
            euler [2] = Math .asin (-Math_Algorithm .clamp (m3, -1, 1));

            if (Math .abs (m3) < 0.9999999)
            {
               euler [0] = Math .atan2 (m5, m4);
               euler [1] = Math .atan2 (m6, m0);

            }
            else
            {
               euler [0] = Math .atan2 (-m7, m8);
               euler [1] = 0;
            }

            break;
         }
      }

      return euler;
   },
   isReal ()
   {
      return !(this .x || this .y || this .z);
   },
   isImag ()
   {
      return !this .w;
   },
   equals ({ x, y, z, w })
   {
      return this .x === x &&
             this .y === y &&
             this .z === z &&
             this .w === w;
   },
   negate ()
   {
      this .x = -this .x;
      this .y = -this .y;
      this .z = -this .z;
      this .w = -this .w;
      return this;
   },
   inverse ()
   {
      this .x = -this .x;
      this .y = -this .y;
      this .z = -this .z;
      return this;
   },
   add ({ x, y, z, w })
   {
      this .x += x;
      this .y += y;
      this .z += z;
      this .w += w;
      return this;
   },
   subtract ({ x, y, z, w })
   {
      this .x -= x;
      this .y -= y;
      this .z -= z;
      this .w -= w;
      return this;
   },
   multiply (value)
   {
      this .x *= value;
      this .y *= value;
      this .z *= value;
      this .w *= value;
      return this;
   },
   multLeft (quat)
   {
      const
         { x: ax, y: ay, z: az, w: aw } = this,
         { x: bx, y: by, z: bz, w: bw } = quat;

      this .x = aw * bx + ax * bw + ay * bz - az * by;
      this .y = aw * by + ay * bw + az * bx - ax * bz;
      this .z = aw * bz + az * bw + ax * by - ay * bx;
      this .w = aw * bw - ax * bx - ay * by - az * bz;

      return this;
   },
   multRight (quat)
   {
      const
         { x: ax, y: ay, z: az, w: aw } = this,
         { x: bx, y: by, z: bz, w: bw } = quat;

      this .x = bw * ax + bx * aw + by * az - bz * ay;
      this .y = bw * ay + by * aw + bz * ax - bx * az;
      this .z = bw * az + bz * aw + bx * ay - by * ax;
      this .w = bw * aw - bx * ax - by * ay - bz * az;

      return this;
   },
   divide (value)
   {
      this .x /= value;
      this .y /= value;
      this .z /= value;
      this .w /= value;
      return this;
   },
   multVecQuat (vector)
   {
      // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Vector_rotation

      const
         { x: qx, y: qy, z: qz, w: qw } = this,
         { x: vx, y: vy, z: vz } = vector,
         tx = 2 * (qy * vz - qz * vy),
         ty = 2 * (qz * vx - qx * vz),
         tz = 2 * (qx * vy - qy * vx);

      vector .x += qw * tx + qy * tz - qz * ty;
      vector .y += qw * ty + qz * tx - qx * tz;
      vector .z += qw * tz + qx * ty - qy * tx;

      return vector;
   },
   multQuatVec (vector)
   {
      // https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Vector_rotation

      const
         { x: qx, y: qy, z: qz, w: qw } = this,
         { x: vx, y: vy, z: vz } = vector,
         tx = 2 * (qz * vy - qy * vz),
         ty = 2 * (qx * vz - qz * vx),
         tz = 2 * (qy * vx - qx * vy);

      vector .x += qw * tx - qy * tz + qz * ty;
      vector .y += qw * ty - qz * tx + qx * tz;
      vector .z += qw * tz - qx * ty + qy * tx;

      return vector;
   },
   normalize ()
   {
      const length = Math .hypot (this .x, this .y, this .z, this .w);

      if (length)
      {
         this .x /= length;
         this .y /= length;
         this .z /= length;
         this .w /= length;
      }

      return this;
   },
   dot (quat)
   {
      return this .x * quat .x +
             this .y * quat .y +
             this .z * quat .z +
             this .w * quat .w;
   },
   squaredNorm ()
   {
      const { x, y, z, w } = this;

      return x * x +
             y * y +
             z * z +
             w * w;
   },
   norm ()
   {
      return Math .hypot (this .x, this .y, this .z, this .w);
   },
   pow (exponent)
   {
      if (exponent instanceof Quaternion)
         return this .assign (e .assign (exponent) .multRight (this .log ()) .exp ());

      if (this .isReal ())
         return this .set (0, 0, 0, this .w ** exponent);

      const
         l     = this .norm (),
         theta = Math .acos (this .w / l),
         li    = this .imag .norm (),
         ltoe  = l ** exponent,
         et    = exponent * theta,
         scale = ltoe / li * Math .sin (et);

      this .x *= scale;
      this .y *= scale;
      this .z *= scale;
      this .w  = ltoe * Math .cos (et);
      return this;
   },
   log ()
   {
      if (this .isReal ())
      {
         if (this .w > 0)
            return this .set (0, 0, 0, Math .log (this .w));

         else
            return this .set (Math .PI, 0, 0, Math .log (-this .w));
      }

      const
         l = this .norm (),
         v = this .imag .normalize () .multiply (Math .acos (this .w / l)),
         w = Math .log (l);

      this .x = v .x;
      this .y = v .y;
      this .z = v .z;
      this .w = w;
      return this;
   },
   exp ()
   {
      if (this .isReal ())
         return this .set (0, 0, 0, Math .exp (this .w));

      const
         i  = this .imag,
         li = i .norm (),
         ew = Math .exp (this .w),
         w  = ew * Math .cos (li),
         v  = i .multiply (ew * Math .sin (li) / li);

      this .x = v .x;
      this .y = v .y;
      this .z = v .z;
      this .w = w;
      return this;
   },
   slerp (destination, t)
   {
      return Math_Algorithm .slerp (this, t1 .assign (destination), t);
   },
   squad (a, b, destination, t)
   {
      // Ken Shoemake defines Squad as:
      // We must use shortest path slerp to prevent flipping. See also spline below.
      // a = spline (si-1, si, si+1) and b = spline (di-1, di, di+1), where si = source and di = destination

      return Math_Algorithm .slerp (Math_Algorithm .slerp (this, t1 .assign (destination), t),
                               Math_Algorithm .slerp (t2 .assign (a), t3 .assign (b), t),
                               2 * t * (1 - t));
   },
   toString ()
   {
      return this .x + " " +
             this .y + " " +
             this .z + " " +
             this .w;
   },
});

for (const key of Object .keys (Quaternion .prototype))
   Object .defineProperty (Quaternion .prototype, key, { enumerable: false });

Object .defineProperties (Quaternion .prototype,
{
   length: { value: 4 },
   0:
   {
      get () { return this .x; },
      set (value) { this .x = value; },
   },
   1:
   {
      get () { return this .y; },
      set (value) { this .y = value; },
   },
   2:
   {
      get () { return this .z; },
      set (value) { this .z = value; },
   },
   3:
   {
      get () { return this .w; },
      set (value) { this .w = value; },
   },
   real:
   {
      get () { return this .w; },
   },
   imag:
   {
      get: (() =>
      {
         const result = new Numbers_Vector3 ();

         return function ()
         {
            return result .set (this .x,
                                this .y,
                                this .z);
         };
      })(),
   },
});

Object .assign (Quaternion,
{
   ZERO: Object .freeze (new Quaternion (0, 0, 0, 0)),
   IDENTITY: Object .freeze (new Quaternion ()),
   spline: (() =>
   {
      const
         q0   = new Quaternion (),
         q1   = new Quaternion (),
         q2   = new Quaternion (),
         q1_i = new Quaternion ();

      return function (Q0, Q1, Q2)
      {
         q0 .assign (Q0);
         q1 .assign (Q1);
         q2 .assign (Q2);

         // If the dot product is smaller than 0 we must negate the quaternion to prevent flipping. If we negate all
         // the terms we get a different quaternion but it represents the same rotation.

         if (q0 .dot (q1) < 0)
            q0 .negate ();

         if (q2 .dot (q1) < 0)
            q2 .negate ();

         q1_i .assign (q1) .inverse ();

         // The result must be normalized as it will be used in slerp and we can only slerp normalized vectors.

         return q1 .multRight (
            t1 .assign (q1_i) .multRight (q0) .log () .add (t2 .assign (q1_i) .multRight (q2) .log ()) .divide (-4) .exp ()
         )
         .normalize () .copy ();
      };
   })(),
});

const
   t1 = new Quaternion (),
   t2 = new Quaternion (),
   t3 = new Quaternion (),
   e  = new Quaternion (),
   Quaternion_m  = new Numbers_Matrix3 ();

const Quaternion_default_ = Quaternion;
;

/* harmony default export */ const Numbers_Quaternion = (Namespace/* default */.A .add ("Quaternion", Quaternion_default_));
;// ./src/standard/Math/Numbers/Rotation4.js






const
   _x          = Symbol (),
   _y          = Symbol (),
   _z          = Symbol (),
   _angle      = Symbol (),
   _quaternion = Symbol ();

function Rotation4 (x = 0, y = 0, z = 1, angle = 0)
{
   this [_x]     = 0;
   this [_y]     = 0;
   this [_z]     = 1;
   this [_angle] = 0;

   this [_quaternion] = new Numbers_Quaternion ();

   this .set (x, y, z, angle);
}

Object .assign (Rotation4 .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this [_x];
      yield this [_y];
      yield this [_z];
      yield this [_angle];
   },
   copy ()
   {
      const copy = Object .create (Rotation4 .prototype);

      copy [_x]     = this [_x];
      copy [_y]     = this [_y];
      copy [_z]     = this [_z];
      copy [_angle] = this [_angle];

      copy [_quaternion] = this [_quaternion] .copy ();

      return copy;
   },
   assign (rotation)
   {
      this [_x]     = rotation [_x];
      this [_y]     = rotation [_y];
      this [_z]     = rotation [_z];
      this [_angle] = rotation [_angle];

      this [_quaternion] .assign (rotation [_quaternion]);

      return this;
   },
   equals (rotation)
   {
      return this [_quaternion] .equals (rotation [_quaternion]);
   },
   get: (() =>
   {
      const result = new Numbers_Vector4 ();

      return function ()
      {
         const quaternion = this [_quaternion];

         if (Math .abs (quaternion .w) > 1)
         {
            return Numbers_Vector4 .Z_AXIS;
         }
         else
         {
            const
               angle = Math .acos (quaternion .w) * 2,
               scale = Math .sin (angle / 2);

            if (scale === 0)
            {
               return Numbers_Vector4 .Z_AXIS;
            }
            else
            {
               const axis = quaternion .imag .divide (scale);

               return result .set (axis .x,
                                   axis .y,
                                   axis .z,
                                   angle);
            }
         }
      };
   })(),
   set (x = 0, y = 0, z = 1, angle = 0)
   {
      this [_x]     = x;
      this [_y]     = y;
      this [_z]     = z;
      this [_angle] = angle;

      const scale = Math .hypot (x, y, z);

      if (scale === 0)
      {
         this [_quaternion] .set (0, 0, 0, 1);
         return this;
      }

      // Determine quaternion.

      const
         halfTheta = Math_Algorithm .interval (angle / 2, 0, Math .PI),
         aScale    = Math .sin (halfTheta) / scale;

      this [_quaternion] .set (x * aScale,
                               y * aScale,
                               z * aScale,
                               Math .cos (halfTheta));
      return this;
   },
   setAxisAngle (axis, angle)
   {
      return this .set (axis .x, axis .y, axis .z, angle);
   },
   setVectors: (() =>
   {
      const
         from = new Numbers_Vector3 (),
         to   = new Numbers_Vector3 (),
         cv   = new Numbers_Vector3 (),
         t    = new Numbers_Vector3 ();

      return function (fromVec, toVec)
      {
         // https://bitbucket.org/Coin3D/coin/src/abc9f50968c9/src/base/SbRotation.cpp

         from .assign (fromVec) .normalize ();
         to   .assign (toVec)   .normalize ();

         const
            cos_angle = Math_Algorithm .clamp (from .dot (to), -1, 1),
            crossvec  = cv .assign (from) .cross (to) .normalize (),
            crosslen  = crossvec .norm ();

         if (crosslen === 0)
         {
            // Parallel vectors
            // Check if they are pointing in the same direction.
            if (cos_angle > 0)
               this [_quaternion] .set (0, 0, 0, 1); // standard rotation

            // Ok, so they are parallel and pointing in the opposite direction
            // of each other.
            else
            {
               // Try crossing with x axis.
               t .assign (from) .cross (Numbers_Vector3 .X_AXIS);

               // If not ok, cross with y axis.
               if (t .squaredNorm () === 0)
                  t .assign (from) .cross (Numbers_Vector3 .Y_AXIS);

               t .normalize ();

               this [_quaternion] .set (t .x, t .y, t .z, 0);
            }
         }
         else
         {
            // Vectors are not parallel
            // The abs () wrapping is to avoid problems when `dot' "overflows" a tiny wee bit,
            // which can lead to sqrt () returning NaN.
            crossvec .multiply (Math .sqrt (Math .abs (1 - cos_angle) / 2));

            this [_quaternion] .set (crossvec .x,
                                     crossvec .y,
                                     crossvec .z,
                                     Math .sqrt (Math .abs (1 + cos_angle) / 2));
         }

         this .normalize ();

         return this;
      };
   })(),
   getAxis (axis = new Numbers_Vector3 ())
   {
      return axis .set (this [_x], this [_y], this [_z]);
   },
   setAxis (vector)
   {
      this .set (vector .x, vector .y, vector .z, this [_angle]);
   },
   getEuler (euler = [ ], order = "XYZ")
   {
      return this [_quaternion] .getEuler (euler, order);
   },
   setEuler (x, y, z, order = "XYZ")
   {
      // Quaternion is then already normalized.
      this [_quaternion] .setEuler (x, y, z, order);
      this .normalize ();
      return this;
   },
   getMatrix (matrix = new Numbers_Matrix3 ())
   {
      return this [_quaternion] .getMatrix (matrix);
   },
   setMatrix (matrix)
   {
      this [_quaternion] .setMatrix (matrix) .normalize ();
      this .normalize ();
      return this;
   },
   getQuaternion (quaternion = new Numbers_Quaternion ())
   {
      return quaternion .assign (this [_quaternion]);
   },
   setQuaternion (quaternion)
   {
      this [_quaternion] .assign (quaternion) .normalize ();
      this .normalize ();
      return this;
   },
   inverse ()
   {
      this [_quaternion] .inverse ();
      this .normalize ();
      return this;
   },
   multLeft (rotation)
   {
      this [_quaternion] .multLeft (rotation [_quaternion]) .normalize ();
      this .normalize ();
      return this;
   },
   multRight (rotation)
   {
      this [_quaternion] .multRight (rotation [_quaternion]) .normalize ();
      this .normalize ();
      return this;
   },
   multVecRot (vector)
   {
      return this [_quaternion] .multVecQuat (vector);
   },
   multRotVec (vector)
   {
      return this [_quaternion] .multQuatVec (vector);
   },
   normalize ()
   {
      const rotation = this .get ();

      this [_x]     = rotation .x;
      this [_y]     = rotation .y;
      this [_z]     = rotation .z;
      this [_angle] = rotation .w;

      return this;
   },
   slerp (dest, t)
   {
      this [_quaternion] .slerp (dest [_quaternion], t);
      this .normalize ();
      return this;
   },
   squad (a, b, dest, t)
   {
      this [_quaternion] .squad (a [_quaternion], b [_quaternion], dest [_quaternion], t);
      this .normalize ();
      return this;
   },
   /**
    * Straightens the rotation so that the x-axis of this rotation is parallel to the plane spawned by upVector.
    */
   straighten: (() =>
   {
      const
         localXAxis = new Numbers_Vector3 (),
         localZAxis = new Numbers_Vector3 (),
         upNormal   = new Numbers_Vector3 ();

      return function (upVector = Numbers_Vector3 .Y_AXIS)
      {
         upNormal .assign (upVector) .normalize ();

         this .multVecRot (localXAxis .assign (Numbers_Vector3 .X_AXIS) .negate ());
         this .multVecRot (localZAxis .assign (Numbers_Vector3 .Z_AXIS));

         // If viewer looks along up vector.
         if (Math .abs (localZAxis .dot (upNormal)) >= 1)
            return this;

         const newXAxis = localZAxis .cross (upNormal) .normalize ();

         if (newXAxis .dot (localXAxis) <= -1)
         {
            rotation .setAxisAngle (Numbers_Vector3 .Z_AXIS, Math .PI);

            return this .multLeft (rotation);
         }
         else
         {
            rotation .setVectors (localXAxis, newXAxis);

            return this .multRight (rotation);
         }
      };
   })(),
   toString ()
   {
      return this [_x] + " " +
             this [_y] + " " +
             this [_z] + " " +
             this [_angle];
   }
});

for (const key of Object .keys (Rotation4 .prototype))
   Object .defineProperty (Rotation4 .prototype, key, { enumerable: false });

const x = {
   get ()
   {
      return this [_x];
   },
   set (x)
   {
      this .set (x, this [_y], this [_z], this [_angle]);
   },
   enumerable: true,
};

const y = {
   get ()
   {
      return this [_y];
   },
   set (y)
   {
      this .set (this [_x], y, this [_z], this [_angle]);
   },
   enumerable: true,
};

const Rotation4_z = {
   get ()
   {
      return this [_z];
   },
   set (z)
   {
      this .set (this [_x], this [_y], z, this [_angle]);
   },
   enumerable: true,
};

const angle = {
   get ()
   {
      return this [_angle];
   },
   set (angle)
   {
      this .set (this [_x], this [_y], this [_z], angle);
   },
   enumerable: true,
};

Object .defineProperties (Rotation4 .prototype,
{
   length: { value: 4 },
   x: x,
   y: y,
   z: Rotation4_z,
   angle: angle,
});

x     .enumerable = false;
y     .enumerable = false;
Rotation4_z     .enumerable = false;
angle .enumerable = false;

Object .defineProperties (Rotation4 .prototype,
{
   0: x,
   1: y,
   2: Rotation4_z,
   3: angle,
});

Object .assign (Rotation4,
{
   IDENTITY: Object .freeze (new Rotation4 ()),
   fromVectors (fromVec, toVec)
   {
      return new Rotation4 () .setVectors (fromVec, toVec);
   },
   fromQuaternion (quaternion)
   {
      return new Rotation4 () .setQuaternion (quaternion);
   },
   fromMatrix (matrix)
   {
      return new Rotation4 () .setMatrix (matrix);
   },
   fromEuler (x, y, z, order = "XYZ")
   {
      return new Rotation4 () .setEuler (x, y, z, order);
   },
   spline (r0, r1, r2)
   {
      const copy = Object .create (this .prototype);
      copy [_quaternion] = Numbers_Quaternion .spline (r0 [_quaternion], r1 [_quaternion], r2 [_quaternion]);
      copy .normalize ();
      return copy;
   },
});

const rotation = new Rotation4 ();

const Rotation4_default_ = Rotation4;
;

/* harmony default export */ const Numbers_Rotation4 = (Namespace/* default */.A .add ("Rotation4", Rotation4_default_));
;// ./src/standard/Math/Numbers/Matrix4.js







function Matrix4_Matrix4 (m00 = 1, m01 = 0,   m02 = 0,   m03 = 0,
                  m10 = 0, m11 = m00, m12 = 0,   m13 = 0,
                  m20 = 0, m21 = 0,   m22 = m11, m23 = 0,
                  m30 = 0, m31 = 0,   m32 = 0,   m33 = m22)
{
   this [ 0] = m00;
   this [ 1] = m01;
   this [ 2] = m02;
   this [ 3] = m03;
   this [ 4] = m10;
   this [ 5] = m11;
   this [ 6] = m12;
   this [ 7] = m13;
   this [ 8] = m20;
   this [ 9] = m21;
   this [10] = m22;
   this [11] = m23;
   this [12] = m30;
   this [13] = m31;
   this [14] = m32;
   this [15] = m33;
}

Object .assign (Matrix4_Matrix4 .prototype,
{
   *[Symbol .iterator] ()
   {
      for (let i = 0; i < 16; ++ i)
         yield this [i];
   },
   copy ()
   {
      const copy = Object .create (Matrix4_Matrix4 .prototype);

      for (let i = 0; i < 16; ++ i)
         copy [i] = this [i];

      return copy;
   },
   assign (matrix)
   {
      for (let i = 0; i < 16; ++ i)
         this [i] = matrix [i];

      return this;
   },
   equals (matrix)
   {
      for (let i = 0; i < 16; ++ i)
      {
         if (this [i] !== matrix [i])
            return false;
      }

      return true;
   },
   set1 (r, c, value)
   {
      this [r * this .order + c] = value;

      return this;
   },
   get1 (r, c)
   {
      return this [r * this .order + c];
   },
   set (m00 = 1, m01 = 0,   m02 = 0,   m03 = 0,
        m10 = 0, m11 = m00, m12 = 0,   m13 = 0,
        m20 = 0, m21 = 0,   m22 = m11, m23 = 0,
        m30 = 0, m31 = 0,   m32 = 0,   m33 = m22)
   {
      this [ 0] = m00;
      this [ 1] = m01;
      this [ 2] = m02;
      this [ 3] = m03;
      this [ 4] = m10;
      this [ 5] = m11;
      this [ 6] = m12;
      this [ 7] = m13;
      this [ 8] = m20;
      this [ 9] = m21;
      this [10] = m22;
      this [11] = m23;
      this [12] = m30;
      this [13] = m31;
      this [14] = m32;
      this [15] = m33;

      return this;
   },
   getTransform: (() =>
   {
      const c = new Numbers_Vector3 ();

      return function (translation, rotation, scale, scaleOrientation, center)
      {
         if (center)
         {
            Matrix4_m .setTransform (c .assign (center) .negate ());
            Matrix4_m .multLeft (this);
            Matrix4_m .translate (center);
            Matrix4_m .getTransform (translation, rotation, scale, scaleOrientation);
         }
         else
         {
            this .factor (translation, rotation, scale, scaleOrientation);
         }

         return this;
      };
   })(),
   setTransform: (() =>
   {
      const
         invScaleOrientation = new Numbers_Rotation4 (),
         invCenter           = new Numbers_Vector3 ();

      return function (translation, rotation, scale, scaleOrientation, center)
      {
         this .set ();

         // P' = T * C * R * SR * S * -SR * -C * P
         if (translation ?.equals (Numbers_Vector3 .ZERO) === false)
            this .translate (translation);

         const hasCenter = center ?.equals (Numbers_Vector3 .ZERO) === false;

         if (hasCenter)
            this .translate (center);

         if (rotation ?.equals (Numbers_Rotation4 .IDENTITY) === false)
            this .rotate (rotation);

         if (scale ?.equals (Numbers_Vector3 .ONE) === false)
         {
            if (scaleOrientation ?.equals (Numbers_Rotation4 .IDENTITY) === false)
            {
               this .rotate (scaleOrientation);
               this .scale (scale);
               this .rotate (invScaleOrientation .assign (scaleOrientation) .inverse ());
            }
            else
            {
               this .scale (scale);
            }
         }

         if (hasCenter)
            this .translate (invCenter .assign (center) .negate ());

         return this;
      };
   })(),
   setRotation (rotation)
   {
      return this .setQuaternion (rotation .getQuaternion (q));
   },
   setQuaternion (quaternion)
   {
      const
         { x, y, z, w } = quaternion,
         yy = y * y,
         zz = z * z,
         xy = x * y,
         zw = z * w,
         xz = x * z,
         yw = y * w,
         xx = x * x,
         yz = y * z,
         xw = x * w;

      this [0]  = 1 - 2 * (yy + zz);
      this [1]  = 2 * (xy + zw);
      this [2]  = 2 * (xz - yw);
      this [3]  = 0;
      this [4]  = 2 * (xy - zw);
      this [5]  = 1 - 2 * (zz + xx);
      this [6]  = 2 * (yz + xw);
      this [7]  = 0;
      this [8]  = 2 * (xz + yw);
      this [9]  = 2 * (yz - xw);
      this [10] = 1 - 2 * (yy + xx);
      this [11] = 0;
      this [12] = 0;
      this [13] = 0;
      this [14] = 0;
      this [15] = 1;

      return this;
   },
   factor: (() =>
   {
      const
         s  = new Numbers_Vector3 (),
         si = new Numbers_Matrix3 (),
         so = new Numbers_Matrix3 (),
         b  = new Numbers_Matrix3 ();

      const eigen = { values: [ ], vectors: [[ ], [ ], [ ]] };

      return function (translation, rotation, scale, scaleOrientation)
      {
         // (1) Get translation.
         translation ?.set (this [12], this [13], this [14]);

         if (!(rotation || scale || scaleOrientation))
            return;

         // (2) Create 3x3 matrix.
         const a = this .submatrix;

         // (3) Compute det A. If negative, set sign = -1, else sign = 1
         const det      = a .determinant ();
         const det_sign = det < 0 ? -1 : 1;

         // (4) B = A * !A  (here !A means A transpose)
         b .assign (a) .transpose () .multLeft (a);
         const e = Algorithms_eigen_decomposition (b, eigen);

         // Find min / max eigenvalues and do ratio test to determine singularity.

         so .set (e .vectors [0] [0], e .vectors [1] [0], e .vectors [2] [0],
                  e .vectors [0] [1], e .vectors [1] [1], e .vectors [2] [1],
                  e .vectors [0] [2], e .vectors [1] [2], e .vectors [2] [2]);

         scaleOrientation ?.setMatrix (so);

         // Compute s = sqrt(eigen values), with sign. Set si = s-inverse

         s .set (det_sign * Math .sqrt (e .values [0]),
                 det_sign * Math .sqrt (e .values [1]),
                 det_sign * Math .sqrt (e .values [2]));

         scale ?.assign (s);

         if (rotation)
         {
            si [0] = 1 / s .x;
            si [4] = 1 / s .y;
            si [8] = 1 / s .z;

            // (5) Compute U = !R ~S R A.
            rotation .setMatrix (a .multLeft (so) .multLeft (si) .multLeft (so .transpose ()));
         }
      };
   })(),
   determinant3 ()
   {
      const { 0: m00, 1: m01, 2: m02,
              4: m04, 5: m05, 6: m06,
              8: m08, 9: m09, 10: m10 } = this;

      return m00 * (m05 * m10 - m06 * m09) -
             m01 * (m04 * m10 - m06 * m08) +
             m02 * (m04 * m09 - m05 * m08);
   },
   determinant ()
   {
      const
         { 0: m00, 1: m01, 2: m02, 3: m03, 4: m04, 5: m05, 6: m06, 7: m07,
           8: m08, 9: m09, 10: m10, 11: m11, 12: m12, 13: m13, 14: m14, 15: m15 } = this;

      const
         b0 = m00 * m05 - m01 * m04,
         b1 = m00 * m06 - m02 * m04,
         b2 = m01 * m06 - m02 * m05,
         b3 = m08 * m13 - m09 * m12,
         b4 = m08 * m14 - m10 * m12,
         b5 = m09 * m14 - m10 * m13,
         b6 = m00 * b5 - m01 * b4 + m02 * b3,
         b7 = m04 * b5 - m05 * b4 + m06 * b3,
         b8 = m08 * b2 - m09 * b1 + m10 * b0,
         b9 = m12 * b2 - m13 * b1 + m14 * b0;

      // Calculate the determinant.
      return m07 * b6 - m03 * b7 + m15 * b8 - m11 * b9;
   },
   transpose ()
   {
      let tmp;

      tmp = this [ 1]; this [ 1] = this [ 4]; this [ 4] = tmp;
      tmp = this [ 2]; this [ 2] = this [ 8]; this [ 8] = tmp;
      tmp = this [ 3]; this [ 3] = this [12]; this [12] = tmp;
      tmp = this [ 6]; this [ 6] = this [ 9]; this [ 9] = tmp;
      tmp = this [ 7]; this [ 7] = this [13]; this [13] = tmp;
      tmp = this [11]; this [11] = this [14]; this [14] = tmp;

      return this;
   },
   inverse ()
   {
      const
         { 0: m00, 1: m01, 2: m02, 3: m03, 4: m04, 5: m05, 6: m06, 7: m07,
           8: m08, 9: m09, 10: m10, 11: m11, 12: m12, 13: m13, 14: m14, 15: m15 } = this;

      const
         b00 = m00 * m05 - m01 * m04,
         b01 = m00 * m06 - m02 * m04,
         b02 = m00 * m07 - m03 * m04,
         b03 = m01 * m06 - m02 * m05,
         b04 = m01 * m07 - m03 * m05,
         b05 = m02 * m07 - m03 * m06,
         b06 = m08 * m13 - m09 * m12,
         b07 = m08 * m14 - m10 * m12,
         b08 = m08 * m15 - m11 * m12,
         b09 = m09 * m14 - m10 * m13,
         b10 = m09 * m15 - m11 * m13,
         b11 = m10 * m15 - m11 * m14;

      // Calculate the determinant.
      let d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

      if (!d)
         return this .assign (Matrix4_Matrix4 .ZERO);

      d = 1 / d;

      this [ 0] = (m05 * b11 - m06 * b10 + m07 * b09) * d;
      this [ 1] = (m02 * b10 - m01 * b11 - m03 * b09) * d;
      this [ 2] = (m13 * b05 - m14 * b04 + m15 * b03) * d;
      this [ 3] = (m10 * b04 - m09 * b05 - m11 * b03) * d;
      this [ 4] = (m06 * b08 - m04 * b11 - m07 * b07) * d;
      this [ 5] = (m00 * b11 - m02 * b08 + m03 * b07) * d;
      this [ 6] = (m14 * b02 - m12 * b05 - m15 * b01) * d;
      this [ 7] = (m08 * b05 - m10 * b02 + m11 * b01) * d;
      this [ 8] = (m04 * b10 - m05 * b08 + m07 * b06) * d;
      this [ 9] = (m01 * b08 - m00 * b10 - m03 * b06) * d;
      this [10] = (m12 * b04 - m13 * b02 + m15 * b00) * d;
      this [11] = (m09 * b02 - m08 * b04 - m11 * b00) * d;
      this [12] = (m05 * b07 - m04 * b09 - m06 * b06) * d;
      this [13] = (m00 * b09 - m01 * b07 + m02 * b06) * d;
      this [14] = (m13 * b01 - m12 * b03 - m14 * b00) * d;
      this [15] = (m08 * b03 - m09 * b01 + m10 * b00) * d;

      return this;
   },
   multLeft (matrix)
   {
      // Complexity 48 +, 64 *.

      const {
         0:  a00, 1:  a01, 2:  a02, 3:  a03,
         4:  a04, 5:  a05, 6:  a06, 7:  a07,
         8:  a08, 9:  a09, 10: a10, 11: a11,
         12: a12, 13: a13, 14: a14, 15: a15
      } = this;

      var { 0: b0, 1: b1, 2: b2, 3: b3 } = matrix;

      this [ 0] = a00 * b0 + a04 * b1 + a08 * b2 + a12 * b3;
      this [ 1] = a01 * b0 + a05 * b1 + a09 * b2 + a13 * b3;
      this [ 2] = a02 * b0 + a06 * b1 + a10 * b2 + a14 * b3;
      this [ 3] = a03 * b0 + a07 * b1 + a11 * b2 + a15 * b3;

      var { 4: b0, 5: b1, 6: b2, 7: b3 } = matrix;

      this [ 4] = a00 * b0 + a04 * b1 + a08 * b2 + a12 * b3;
      this [ 5] = a01 * b0 + a05 * b1 + a09 * b2 + a13 * b3;
      this [ 6] = a02 * b0 + a06 * b1 + a10 * b2 + a14 * b3;
      this [ 7] = a03 * b0 + a07 * b1 + a11 * b2 + a15 * b3;

      var { 8: b0, 9: b1, 10: b2, 11: b3 } = matrix;

      this [ 8] = a00 * b0 + a04 * b1 + a08 * b2 + a12 * b3;
      this [ 9] = a01 * b0 + a05 * b1 + a09 * b2 + a13 * b3;
      this [10] = a02 * b0 + a06 * b1 + a10 * b2 + a14 * b3;
      this [11] = a03 * b0 + a07 * b1 + a11 * b2 + a15 * b3;

      var { 12: b0, 13: b1, 14: b2, 15: b3 } = matrix;

      this [12] = a00 * b0 + a04 * b1 + a08 * b2 + a12 * b3;
      this [13] = a01 * b0 + a05 * b1 + a09 * b2 + a13 * b3;
      this [14] = a02 * b0 + a06 * b1 + a10 * b2 + a14 * b3;
      this [15] = a03 * b0 + a07 * b1 + a11 * b2 + a15 * b3;

      return this;
   },
   multRight (matrix)
   {
      // Complexity 48 +, 64 *.

      const {
         0:  b00, 1:  b01, 2:  b02, 3:  b03,
         4:  b04, 5:  b05, 6:  b06, 7:  b07,
         8:  b08, 9:  b09, 10: b10, 11: b11,
         12: b12, 13: b13, 14: b14, 15: b15
      } = matrix;

      var { 0: a0, 1: a1, 2: a2, 3: a3 } = this;

      this [ 0] = a0 * b00 + a1 * b04 + a2 * b08 + a3 * b12;
      this [ 1] = a0 * b01 + a1 * b05 + a2 * b09 + a3 * b13;
      this [ 2] = a0 * b02 + a1 * b06 + a2 * b10 + a3 * b14;
      this [ 3] = a0 * b03 + a1 * b07 + a2 * b11 + a3 * b15;

      var { 4: a0, 5: a1, 6: a2, 7: a3 } = this;

      this [ 4] = a0 * b00 + a1 * b04 + a2 * b08 + a3 * b12;
      this [ 5] = a0 * b01 + a1 * b05 + a2 * b09 + a3 * b13;
      this [ 6] = a0 * b02 + a1 * b06 + a2 * b10 + a3 * b14;
      this [ 7] = a0 * b03 + a1 * b07 + a2 * b11 + a3 * b15;

      var { 8: a0, 9: a1, 10: a2, 11: a3 } = this;

      this [ 8] = a0 * b00 + a1 * b04 + a2 * b08 + a3 * b12;
      this [ 9] = a0 * b01 + a1 * b05 + a2 * b09 + a3 * b13;
      this [10] = a0 * b02 + a1 * b06 + a2 * b10 + a3 * b14;
      this [11] = a0 * b03 + a1 * b07 + a2 * b11 + a3 * b15;

      var { 12: a0, 13: a1, 14: a2, 15: a3 } = this;

      this [12] = a0 * b00 + a1 * b04 + a2 * b08 + a3 * b12;
      this [13] = a0 * b01 + a1 * b05 + a2 * b09 + a3 * b13;
      this [14] = a0 * b02 + a1 * b06 + a2 * b10 + a3 * b14;
      this [15] = a0 * b03 + a1 * b07 + a2 * b11 + a3 * b15;

      return this;
   },
   multVecMatrix (vector)
   {
      if (vector .length === 3)
      {
         const
            { x, y, z } = vector,
            w = 1 / (x * this [3] + y * this [7] + z * this [11] + this [15]);

         vector .x = (x * this [0] + y * this [4] + z * this [ 8] + this [12]) * w;
         vector .y = (x * this [1] + y * this [5] + z * this [ 9] + this [13]) * w;
         vector .z = (x * this [2] + y * this [6] + z * this [10] + this [14]) * w;

         return vector;
      }
      else
      {
         const { x, y, z, w } = vector;

         vector .x = x * this [0] + y * this [4] + z * this [ 8] + w * this [12];
         vector .y = x * this [1] + y * this [5] + z * this [ 9] + w * this [13];
         vector .z = x * this [2] + y * this [6] + z * this [10] + w * this [14];
         vector .w = x * this [3] + y * this [7] + z * this [11] + w * this [15];

         return vector;
      }
   },
   multMatrixVec (vector)
   {
      if (vector .length === 3)
      {
         const
            { x, y, z } = vector,
            w = 1 / (x * this [12] + y * this [13] + z * this [14] + this [15]);

         vector .x = (x * this [0] + y * this [1] + z * this [ 2] + this [ 3]) * w;
         vector .y = (x * this [4] + y * this [5] + z * this [ 6] + this [ 7]) * w;
         vector .z = (x * this [8] + y * this [9] + z * this [10] + this [11]) * w;

         return vector;
      }
      else
      {
         const { x, y, z, w } = vector;

         vector .x = x * this [ 0] + y * this [ 1] + z * this [ 2] + w * this [ 3];
         vector .y = x * this [ 4] + y * this [ 5] + z * this [ 6] + w * this [ 7];
         vector .z = x * this [ 8] + y * this [ 9] + z * this [10] + w * this [11];
         vector .w = x * this [12] + y * this [13] + z * this [14] + w * this [15];

         return vector;
      }
   },
   multDirMatrix (vector)
   {
      const { x, y, z } = vector;

      vector .x = x * this [0] + y * this [4] + z * this [ 8];
      vector .y = x * this [1] + y * this [5] + z * this [ 9];
      vector .z = x * this [2] + y * this [6] + z * this [10];

      return vector;
   },
   multMatrixDir (vector)
   {
      const { x, y, z } = vector;

      vector .x = x * this [0] + y * this [1] + z * this [ 2];
      vector .y = x * this [4] + y * this [5] + z * this [ 6];
      vector .z = x * this [8] + y * this [9] + z * this [10];

      return vector;
   },
   translate (translation)
   {
      const { x, y, z } = translation;

      this [12] += this [0] * x + this [4] * y + this [ 8] * z;
      this [13] += this [1] * x + this [5] * y + this [ 9] * z;
      this [14] += this [2] * x + this [6] * y + this [10] * z;

      return this;
   },
   rotate (rotation)
   {
      return this .multLeft (Matrix4_m .setQuaternion (rotation .getQuaternion (q)));
   },
   scale (scale)
   {
      const { x, y, z } = scale;

      this [ 0] *= x;
      this [ 4] *= y;
      this [ 8] *= z;

      this [ 1] *= x;
      this [ 5] *= y;
      this [ 9] *= z;

      this [ 2] *= x;
      this [ 6] *= y;
      this [10] *= z;

      return this;
   },
   toString ()
   {
      return Array .prototype .join .call (this, " ");
   },
});

for (const key of Object .keys (Matrix4_Matrix4 .prototype))
   Object .defineProperty (Matrix4_Matrix4 .prototype, key, { enumerable: false });

Object .defineProperties (Matrix4_Matrix4 .prototype,
{
   order: { value: 4 },
   length: { value: 16 },
   x:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector4 ();

         return function () { return vector .set (this [0], this [1], this [2], this [3]); };
      })(),
   },
   y:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector4 ();

         return function () { return vector .set (this [4], this [5], this [6], this [7]); };
      })(),
   },
   z:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector4 ();

         return function () { return vector .set (this [8], this [9], this [10], this [11]); };
      })(),
   },
   w:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector4 ();

         return function () { return vector .set (this [12], this [13], this [14], this [15]); };
      })(),
   },
   xAxis:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [0], this [1], this [2]); };
      })(),
   },
   yAxis:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [4], this [5], this [6]); };
      })(),
   },
   zAxis:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [8], this [9], this [10]); };
      })(),
   },
   origin:
   {
      get: (() =>
      {
         const vector = new Numbers_Vector3 ();

         return function () { return vector .set (this [12], this [13], this [14]); };
      })(),
   },
   submatrix:
   {
      get: (() =>
      {
         const matrix = new Numbers_Matrix3 ();

         return function ()
         {
            matrix [0] = this [0]; matrix [1] = this [1]; matrix [2] = this [ 2];
            matrix [3] = this [4]; matrix [4] = this [5]; matrix [5] = this [ 6];
            matrix [6] = this [8]; matrix [7] = this [9]; matrix [8] = this [10];
            return matrix;
         };
      })(),
   },
});

Object .assign (Matrix4_Matrix4,
{
   ZERO: Object .freeze (new Matrix4_Matrix4 (0)),
   IDENTITY: Object .freeze (new Matrix4_Matrix4 ()),
   fromTransform (translation, rotation, scale, scaleOrientation, center)
   {
      return Object .create (this .prototype) .setTransform (translation, rotation, scale, scaleOrientation, center);
   },
   fromRotation (rotation)
   {
      return Object .create (this .prototype) .setQuaternion (rotation .getQuaternion (q));
   },
   fromQuaternion (quaternion)
   {
      return Object .create (this .prototype) .setQuaternion (quaternion);
   },
   fromMatrix3 (matrix)
   {
      return new Matrix4_Matrix4 (matrix [0], matrix [1], 0, 0,
                          matrix [3], matrix [4], 0, 0,
                          0, 0, 1, 0,
                          matrix [6], matrix [7], 0, 1);
   },
   fromSubMatrix (matrix)
   {
      return new Matrix4_Matrix4 (matrix [0], matrix [1], matrix [2], 0,
                          matrix [3], matrix [4], matrix [5], 0,
                          matrix [6], matrix [7], matrix [8], 0,
                          0, 0, 0, 1);
   },
});

const
   q = new Numbers_Quaternion (),
   Matrix4_m = new Matrix4_Matrix4 ();

const Matrix4_default_ = Matrix4_Matrix4;
;

/* harmony default export */ const Numbers_Matrix4 = (Namespace/* default */.A .add ("Matrix4", Matrix4_default_));
;// ./src/x_ite/Fields/SFMatrix4.js




function SFMatrix4Template (TypeName, double)
{
   function SFMatrix4 (m00 = 1, m01 = 0, m02 = 0, m03 = 0,
                       m10 = 0, m11 = 1, m12 = 0, m13 = 0,
                       m20 = 0, m21 = 0, m22 = 1, m23 = 0,
                       m30 = 0, m31 = 0, m32 = 0, m33 = 1)
   {
      Base_X3DField .call (this, new Numbers_Matrix4 (+m00, +m01, +m02, +m03,
                                         +m10, +m11, +m12, +m13,
                                         +m20, +m21, +m22, +m23,
                                         +m30, +m31, +m32, +m33));
   }

   return Fields_SFMatrixPrototypeTemplate (SFMatrix4, TypeName, Numbers_Matrix4, double);
}

const SFMatrix4 = {
   SFMatrix4d: SFMatrix4Template ("SFMatrix4d", true),
   SFMatrix4f: SFMatrix4Template ("SFMatrix4f", false),
   VrmlMatrix: SFMatrix4Template ("VrmlMatrix", false),
};

const SFMatrix4_default_ = SFMatrix4;
;

/* harmony default export */ const Fields_SFMatrix4 = (Namespace/* default */.A .add ("SFMatrix4", SFMatrix4_default_));
;// ./src/x_ite/Fields/SFNodeCache.js


const cache = new WeakMap ();

// const r = new FinalizationRegistry (t => console .error (`object deleted ${--i} ${t}`));
// let i = 0;

const SFNodeCache =
{
   get (baseNode)
   {
      const node = cache .get (baseNode);

      if (node)
      {
         return node;
      }
      else
      {
         const node = new Fields_SFNode (baseNode);

         this .set (baseNode, node);

         return node;
      }
   },
   set (baseNode, node)
   {
      Object .defineProperties (node, properties);

      cache .set (baseNode, node);
   },
   delete (baseNode)
   {
      cache .delete (baseNode);
   },
};

const disable =
{
   value: undefined,
   configurable: true,
};

const properties =
{
   fromString: disable,
   fromVRMLString: disable,
   fromXMLString: disable,
   fromJSONString: disable,
   dispose:
   {
      value ()
      {
         this .getValue () ?.dispose ();

         Fields_SFNode .prototype .dispose .call (this);
      },
      configurable: true,
   },
};

const SFNodeCache_default_ = SFNodeCache;
;

/* harmony default export */ const Fields_SFNodeCache = (Namespace/* default */.A .add ("SFNodeCache", SFNodeCache_default_));
;// ./src/x_ite/Fields/SFNode.js




const
   _target = Symbol (),
   _proxy  = Symbol ();

const SFNode_handler =
{
   get (target, key, receiver)
   {
      if (typeof key === "string")
      {
         const
            node  = target .getValue (),
            field = node ?.getField (key, false);

         if (field)
         {
            // Specification conform would be: accessType & X3DConstants .outputOnly.
            // But we allow read access to plain fields, too.
            if (field .getAccessType () === Base_X3DConstants .inputOnly)
               return undefined;

            return field .valueOf ();
         }
      }

      return Reflect .get (target, key, receiver);
   },
   set (target, key, value, receiver)
   {
      if (typeof key === "string")
      {
         const
            node  = target .getValue (),
            field = node ?.getField (key, false);

         if (field)
         {
            const accessType = field .getAccessType ();

            if (accessType === Base_X3DConstants .outputOnly)
               return false;

            field .setValue (value);
            return true;
         }
      }

      return Reflect .set (target, key, value, receiver);
   },
   has (target, key)
   {
      return Boolean (target .getValue () .getField (key, false))
         || Reflect .has (target, key);
   },
   ownKeys (target)
   {
      const
         node    = target .getValue (),
         ownKeys = [ ];

      if (node)
      {
         for (const { name } of node .getFieldDefinitions ())
            ownKeys .push (name);
      }

      return ownKeys .concat (Reflect .ownKeys (target));
   },
   getOwnPropertyDescriptor (target, key)
   {
      if (typeof key === "string")
      {
         const node = target .getValue ();

         if (node)
         {
            const fieldDefinition = node .getFieldDefinitions () .get (key);

            if (fieldDefinition)
            {
               return {
                  value: this .get (target, key),
                  writable: fieldDefinition .accessType !== Base_X3DConstants .outputOnly,
                  enumerable: true,
                  configurable: true,
               };
            }
         }
      }

      return Reflect .getOwnPropertyDescriptor (target, key);
   },
};

function SFNode (node)
{
   // Node need to test for X3DBaseNode, because there is a special version of SFNode in Script.

   const proxy = new Proxy (this, SFNode_handler);

   this [_target] = this;
   this [_proxy]  = proxy;

   if (node ?.getType () .includes (Base_X3DConstants .X3DNode))
   {
      node .addParent (proxy);

      Base_X3DField .call (this, node);
   }
   else
   {
      Base_X3DField .call (this, null);
   }

   return proxy;
}

Object .assign (Object .setPrototypeOf (SFNode .prototype, Base_X3DField .prototype),
{
   [_target]: null,
   [_proxy]: null,
   copy (instance)
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node && instance)
      {
         const copy = node .copy (instance);

         if (!copy .isInitialized () && !instance .getExecutionContext () .getOuterNode () ?.getType () .includes (Base_X3DConstants .X3DProtoDeclaration))
         {
            copy .setup ();
         }

         return new SFNode (copy);
      }

      return new SFNode (node);
   },
   equals (node)
   {
      const target = this [_target];

      if (node)
         return target .getValue () === node .getValue ();

      return target .getValue () === null;
   },
   isDefaultValue ()
   {
      const target = this [_target];

      return target .getValue () === null;
   },
   set (node)
   {
      const
         target  = this [_target],
         current = target .getValue ();

      current ?.removeParent (target [_proxy]);

      // No need to test for X3DBaseNode, because there is a special version of SFNode in Script.

      if (node ?.getType () .includes (Base_X3DConstants .X3DNode))
      {
         node .addParent (target [_proxy]);

         Base_X3DField .prototype .set .call (target, node);
      }
      else
      {
         Base_X3DField .prototype .set .call (target, null);
      }
   },
   getNodeTypeName ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         return node .getTypeName ();

      throw new Error ("SFNode.getNodeTypeName: node is null.");
   },
   getNodeName ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         return node .getName ();

      throw new Error ("SFNode.getNodeName: node is null.");
   },
   getNodeDisplayName ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         return node .getDisplayName ();

      throw new Error ("SFNode.getNodeDisplayName: node is null.");
   },
   getNodeType ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         return Array .from (node .getType ());

      throw new Error ("SFNode.getNodeType: node is null.");
   },
   getFieldDefinition (name)
   {
      const fieldDefinition = this .getFieldDefinitions () .get (name);

      if (fieldDefinition)
         return fieldDefinition;

      throw new Error (`Unknown field '${name}' in node class ${this .getNodeTypeName ()}.`);
   },
   getFieldDefinitions ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         return node .getFieldDefinitions ();

      throw new Error ("SFNode.getFieldDefinitions: node is null.");
   },
   /**
   * @deprecated Returns the corresponding X3DField object associated with *name*. Use sfnode.{fieldName} syntax.
   */
   getField: (() =>
   {
      let warn = true;

      return function (name)
      {
         if (warn)
         {
            warn = false;

            const target = { };

            Error .captureStackTrace (target, this .getField);

            console .warn ("The use of sfnode.getField(name) is deprecated. Future versions of X_ITE may remove this feature. Instead, use the sfnode.{fieldName} syntax or sfnode.addFieldCallback(key, fieldName, callback).", target .stack);
         }

         const
            target = this [_target],
            node   = target .getValue ();

         if (node)
            return node .getField (name);

         throw new Error ("SFNode is disposed.")
      };
   })(),
   addFieldCallback (... args)
   {
      const target = this [_target];

      switch (args .length)
      {
         case 2:
         {
            const [key, callback] = args;

            return Base_X3DField .prototype .addFieldCallback .call (target, key, callback);
         }
         case 3:
         {
            const
               [key, name, callback] = args,
               node                  = target .getValue ();

            if (node)
               return node .getField (name) .addFieldCallback (key, callback);

            throw new Error ("SFNode.addFieldCallback: node is null.");
         }
      }
   },
   removeFieldCallback (... args)
   {
      const target = this [_target];

      switch (args .length)
      {
         case 1:
         {
            const [key] = args;

            return Base_X3DField .prototype .removeFieldCallback .call (target, key);
         }
         case 2:
         {
            const
               [key, name] = args,
               node        = target .getValue ();

            if (node)
               return node .getField (name) .removeFieldCallback (key);

            throw new Error ("SFNode.removeFieldCallback: node is null.");
         }
      }
   },
   getNodeUserData (key)
   {
      const node = this [_target] .getValue ();

      if (node)
         return node .getUserData (key);

      throw new Error ("SFNode.getNodeUserData: node is null.");
   },
   setNodeUserData (key, data)
   {
      const node = this [_target] .getValue ();

      if (node)
         return node .setUserData (key, data);

      throw new Error ("SFNode.setNodeUserData: node is null.");
   },
   removeNodeUserData (key)
   {
      const node = this [_target] .getValue ();

      if (node)
         return node .removeUserData (key);

      throw new Error ("SFNode.removeNodeUserData: node is null.");
   },
   valueOf ()
   {
      const
         target = this [_target],
         node   = target .getValue ();

      return node ? Fields_SFNodeCache .get (node) : null;
   },
   toStream (generator)
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         node .toStream (generator);
      else
         generator .NULL ();
   },
   toVRMLStream (generator)
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         node .toVRMLStream (generator);
      else
         generator .NULL ();
   },
   toXMLStream (generator)
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         node .toXMLStream (generator);
      else
         generator .string += "<!-- NULL -->";
   },
   toJSONStream (generator)
   {
      this .toJSONStreamValue (generator);
   },
   toJSONStreamValue (generator)
   {
      const
         target = this [_target],
         node   = target .getValue ();

      if (node)
         node .toJSONStream (generator);
      else
         generator .string += "null";
   },
   dispose ()
   {
      const target = this [_target];

      target .set (null);
      target .processInterests ();

      Base_X3DField .prototype .dispose .call (target);
   },
});

for (const key of Object .keys (SFNode .prototype))
   Object .defineProperty (SFNode .prototype, key, { enumerable: false });

Base_X3DField .addStaticProperties (SFNode, "SFNode");

const SFNode_default_ = SFNode;
;

/* harmony default export */ const Fields_SFNode = (Namespace/* default */.A .add ("SFNode", SFNode_default_));
;// ./src/x_ite/Fields/SFQuaternion.js




const { SFMatrix3f } = Fields_SFMatrix3;

function SFQuaternion (x = 0, y = 0, z = 0, w = 0)
{
   Base_X3DField .call (this, new Numbers_Quaternion (+x, +y, +z, +w));
}

Object .assign (Object .setPrototypeOf (SFQuaternion .prototype, Base_X3DField .prototype),
{
   *[Symbol .iterator] ()
   {
      yield* this .getValue ();
   },
   copy ()
   {
      return SFQuaternion .fromValue (this .getValue () .copy ());
   },
   equals (quaternion)
   {
      return this .getValue () .equals (quaternion .getValue ());
   },
   isDefaultValue ()
   {
      return this .getValue () .equals (Numbers_Quaternion .ZERO);
   },
   set (value)
   {
      this .getValue () .assign (value);
   },
   add (quaternion)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .add (quaternion .getValue ()));
   },
   divide (value)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .divide (value));
   },
   dot (quaternion)
   {
      return this .getValue () .dot (quaternion);
   },
   getMatrix ()
   {
      return SFMatrix3f .fromValue (this .getValue () .getMatrix ());
   },
   inverse ()
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .inverse ());
   },
   length ()
   {
      return this .getValue () .norm ();
   },
   multiply (value)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .multiply (value));
   },
   multQuat (quaternion)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .multRight (quaternion .getValue ()));
   },
   multVec (vector)
   {
      return vector .constructor .fromValue (this .getValue () .multVecQuat (vector .getValue () .copy ()));
   },
   negate ()
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .negate ());
   },
   normalize ()
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .normalize ());
   },
   setMatrix (matrix)
   {
      this .getValue () .setMatrix (matrix .getValue ());
      this .addEvent ();
   },
   slerp (quaternion, t)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .slerp (quaternion .getValue (), t));
   },
   subtract (quaternion)
   {
      return SFQuaternion .fromValue (this .getValue () .copy () .subtract (quaternion .getValue ()));
   },
   toStream (generator)
   {
      const { x, y, z, w } = this .getValue ();

      generator .string += generator .DoubleFormat (x);
      generator .Space ();
      generator .string += generator .DoubleFormat (y);
      generator .Space ();
      generator .string += generator .DoubleFormat (z);
      generator .Space ();
      generator .string += generator .DoubleFormat (w);
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      this .toStream (generator);
   },
   toJSONStream (generator)
   {
      generator .string += '[';
      generator .TidySpace ();

      this .toJSONStreamValue (generator);

      generator .TidySpace ();
      generator .string += ']';
   },
   toJSONStreamValue (generator)
   {
      const { x, y, z, w } = this .getValue ();

      generator .string += generator .Number (generator .DoubleFormat (x));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (y));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (z));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (w));
   },
});

for (const key of Object .keys (SFQuaternion .prototype))
   Object .defineProperty (SFQuaternion .prototype, key, { enumerable: false });

const SFQuaternion_x = {
   get ()
   {
      return this .getValue () .x;
   },
   set (value)
   {
      this .getValue () .x = +value;
      this .addEvent ();
   },
};

const SFQuaternion_y = {
   get ()
   {
      return this .getValue () .y;
   },
   set (value)
   {
      this .getValue () .y = +value;
      this .addEvent ();
   },
};

const SFQuaternion_z = {
   get ()
   {
      return this .getValue () .z;
   },
   set (value)
   {
      this .getValue () .z = +value;
      this .addEvent ();
   },
};

const w = {
   get ()
   {
      return this .getValue () .w;
   },
   set (value)
   {
      this .getValue () .w = +value;
      this .addEvent ();
   },
};

Object .defineProperties (SFQuaternion .prototype,
{
   0: SFQuaternion_x,
   1: SFQuaternion_y,
   2: SFQuaternion_z,
   3: w,
   x: Object .assign ({ enumerable: true }, SFQuaternion_x),
   y: Object .assign ({ enumerable: true }, SFQuaternion_y),
   z: Object .assign ({ enumerable: true }, SFQuaternion_z),
   w: Object .assign ({ enumerable: true }, w),
});

Base_X3DField .addStaticProperties (SFQuaternion, "SFQuaternion");

Object .defineProperties (SFQuaternion,
{
   ZERO:
   {
      value: SFQuaternion .fromValue (Numbers_Quaternion .ZERO),
      enumerable: true,
   },
   IDENTITY:
   {
      value: SFQuaternion .fromValue (Numbers_Quaternion .IDENTITY),
      enumerable: true,
   },
   fromMatrix:
   {
      value (matrix)
      {
         const quaternion = new this ();

         quaternion .setMatrix (matrix);

         return quaternion;
      },
   },
});

const SFQuaternion_default_ = SFQuaternion;
;

/* harmony default export */ const Fields_SFQuaternion = (Namespace/* default */.A .add ("SFQuaternion", SFQuaternion_default_));
;// ./src/x_ite/Fields/SFVecPrototypeTemplate.js


function SFVecPrototypeTemplate (Constructor, TypeName, Vector, double, properties = { })
{
   const _formatter = double ? "DoubleFormat" : "FloatFormat";

   Base_X3DField .addStaticProperties (Constructor, TypeName);

   Object .assign (Object .setPrototypeOf (Constructor .prototype, Base_X3DField .prototype),
   {
      *[Symbol .iterator] ()
      {
         yield* this .getValue ();
      },
      copy ()
      {
         return Constructor .fromValue (this .getValue () .copy ());
      },
      equals (vector)
      {
         return this .getValue () .equals (vector .getValue ());
      },
      isDefaultValue ()
      {
         return this .getValue () .equals (Vector .ZERO);
      },
      set (value)
      {
         this .getValue () .assign (value);
      },
      abs ()
      {
         return Constructor .fromValue (this .getValue () .copy () .abs ());
      },
      add (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .add (vector .getValue ()));
      },
      clamp (low, high)
      {
         return Constructor .fromValue (this .getValue () .copy () .clamp (low .getValue (), high .getValue ()));
      },
      distance (vector)
      {
         return this .getValue () .distance (vector .getValue ());
      },
      divide (value)
      {
         return Constructor .fromValue (this .getValue () .copy () .divide (+value));
      },
      divVec (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .divVec (vector .getValue ()));
      },
      dot (vector)
      {
         return this .getValue () .dot (vector .getValue ());
      },
      inverse ()
      {
         return Constructor .fromValue (this .getValue () .copy () .inverse ());
      },
      length ()
      {
         return this .getValue () .norm ();
      },
      lerp (destination, t)
      {
         return Constructor .fromValue (this .getValue () .copy () .lerp (destination, +t));
      },
      max (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .max (vector .getValue ()));
      },
      min (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .min (vector .getValue ()));
      },
      multiply (value)
      {
         return Constructor .fromValue (this .getValue () .copy () .multiply (+value));
      },
      multVec (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .multVec (vector .getValue ()));
      },
      negate ()
      {
         return Constructor .fromValue (this .getValue () .copy () .negate ());
      },
      normalize ()
      {
         return Constructor .fromValue (this .getValue () .copy () .normalize ());
      },
      reflect (normal)
      {
         return Constructor .fromValue (this .getValue () .copy () .reflect (normal .getValue ()));
      },
      subtract (vector)
      {
         return Constructor .fromValue (this .getValue () .copy () .subtract (vector .getValue ()));
      },
      toStream (generator)
      {
         const
            value    = this .getValue (),
            last     = value .length - 1,
            category = this .getUnit ();

         for (let i = 0; i < last; ++ i)
         {
            generator .string += generator [_formatter] (generator .ToUnit (category, value [i]));
            generator .Space ();
         }

         generator .string += generator [_formatter] (generator .ToUnit (category, value [last]));
      },
      toVRMLStream (generator)
      {
         this .toStream (generator);
      },
      toXMLStream (generator)
      {
         this .toStream (generator);
      },
      toJSONStream (generator)
      {
         generator .string += '[';
         generator .TidySpace ();

         this .toJSONStreamValue (generator);

         generator .TidySpace ();
         generator .string += ']';
      },
      toJSONStreamValue (generator)
      {
         const
            value    = this .getValue (),
            last     = value .length - 1,
            category = this .getUnit ();

         for (let i = 0; i < last; ++ i)
         {
            generator .string += generator .Number (generator [_formatter] (generator .ToUnit (category, value [i])));
            generator .string += ',';
            generator .TidySpace ();
         }

         generator .string += generator .Number (generator [_formatter] (generator .ToUnit (category, value [last])));
      },
   },
   properties);

   for (const key of Object .keys (Constructor .prototype))
      Object .defineProperty (Constructor .prototype, key, { enumerable: false });

   const x = {
      get ()
      {
         return this .getValue () .x;
      },
      set (value)
      {
         this .getValue () .x = +value;
         this .addEvent ();
      },
   };

   const y = {
      get ()
      {
         return this .getValue () .y;
      },
      set (value)
      {
         this .getValue () .y = +value;
         this .addEvent ();
      },
   };

   const z = {
      get ()
      {
         return this .getValue () .z;
      },
      set (value)
      {
         this .getValue () .z = +value;
         this .addEvent ();
      },
   };

   const w = {
      get ()
      {
         return this .getValue () .w;
      },
      set (value)
      {
         this .getValue () .w = +value;
         this .addEvent ();
      },
   };

   const indices = [
      [0, x],
      [1, y],
      [2, z],
      [3, w],
   ];

   const props = [
      ["x", Object .assign ({ enumerable: true }, x)],
      ["y", Object .assign ({ enumerable: true }, y)],
      ["z", Object .assign ({ enumerable: true }, z)],
      ["w", Object .assign ({ enumerable: true }, w)],
   ];

   const constants = [
      "X_AXIS",
      "Y_AXIS",
      "Z_AXIS",
      "W_AXIS",
   ];

   const negatives = [
      "NEGATIVE_X_AXIS",
      "NEGATIVE_Y_AXIS",
      "NEGATIVE_Z_AXIS",
      "NEGATIVE_W_AXIS",
   ];

   indices   .length = Vector .prototype .length;
   props     .length = Vector .prototype .length;
   constants .length = Vector .prototype .length;
   negatives .length = Vector .prototype .length;

   Object .defineProperties (Constructor .prototype, Object .fromEntries (indices .concat (props)));

   Object .defineProperties (Constructor, Object .fromEntries ([
      "ONE",
      "NEGATIVE_ONE",
      "ZERO",
      ... constants,
      ... negatives,
   ]
   .map (key => [key, { value: Constructor .fromValue (Vector [key]), enumerable: true }])));

   return Constructor;
}

const SFVecPrototypeTemplate_default_ = SFVecPrototypeTemplate;
;

/* harmony default export */ const Fields_SFVecPrototypeTemplate = (Namespace/* default */.A .add ("SFVecPrototypeTemplate", SFVecPrototypeTemplate_default_));
;// ./src/x_ite/Fields/SFVec3.js




function SFVec3Template (TypeName, double)
{
   function SFVec3 (x = 0, y = 0, z = 0)
   {
      Base_X3DField .call (this, new Numbers_Vector3 (+x, +y, +z));
   }

   Fields_SFVecPrototypeTemplate (SFVec3, TypeName, Numbers_Vector3, double,
   {
      cross (vector)
      {
         return SFVec3 .fromValue (this .getValue () .copy () .cross (vector .getValue ()));
      },
   });

   return SFVec3;
}

const SFVec3 = {
   SFVec3d: SFVec3Template ("SFVec3d", true),
   SFVec3f: SFVec3Template ("SFVec3f", false),
};

const SFVec3_default_ = SFVec3;
;

/* harmony default export */ const Fields_SFVec3 = (Namespace/* default */.A .add ("SFVec3", SFVec3_default_));
;// ./src/x_ite/Fields/SFRotation.js






const
   { SFVec3d, SFVec3f } = Fields_SFVec3,
   { SFMatrix3f: SFRotation_SFMatrix3f }       = Fields_SFMatrix3;

function SFRotation (x = 0, y = 0, z = 1, angle = 0)
{
   if ((x instanceof SFVec3f) || (x instanceof SFVec3d))
   {
      if ((y instanceof SFVec3f) || (y instanceof SFVec3d))
      {
         // new SFRotation (fromVector: SFVec3d | SFVec3f, toVector: SFVec3d | SFVec3f)
         Base_X3DField .call (this, Numbers_Rotation4 .fromVectors (x .getValue (), y .getValue ()));
      }
      else
      {
         // new SFRotation (axis: SFVec3d | SFVec3f, angle: number)
         Base_X3DField .call (this, new Numbers_Rotation4 (... x, +y));
      }
   }
   else
   {
      Base_X3DField .call (this, new Numbers_Rotation4 (+x, +y, +z, +angle));
   }
}

Object .assign (Object .setPrototypeOf (SFRotation .prototype, Base_X3DField .prototype),
{
   *[Symbol .iterator] ()
   {
      yield* this .getValue ();
   },
   copy ()
   {
      return SFRotation .fromValue (this .getValue () .copy ());
   },
   equals (rotation)
   {
      return this .getValue () .equals (rotation .getValue ());
   },
   isDefaultValue ()
   {
      return this .getValue () .equals (Numbers_Rotation4 .IDENTITY);
   },
   set (value)
   {
      this .getValue () .assign (value);
   },
   getAxis ()
   {
      return SFVec3f .fromValue (this .getValue () .getAxis ());
   },
   getMatrix ()
   {
      return SFRotation_SFMatrix3f .fromValue (this .getValue () .getMatrix ());
   },
   getQuaternion ()
   {
      return Fields_SFQuaternion .fromValue (this .getValue () .getQuaternion ());
   },
   inverse ()
   {
      return SFRotation .fromValue (this .getValue () .copy () .inverse ());
   },
   multiply (rotation)
   {
      return SFRotation .fromValue (this .getValue () .copy () .multRight (rotation .getValue ()));
   },
   multVec (vector)
   {
      return vector .constructor .fromValue (this .getValue () .multVecRot (vector .getValue () .copy ()));
   },
   setAxis (vector)
   {
      this .getValue () .setAxis (vector .getValue ());
      this .addEvent ();
   },
   setMatrix (matrix)
   {
      this .getValue () .setMatrix (matrix .getValue ());
      this .addEvent ();
   },
   setQuaternion (quaternion)
   {
      this .getValue () .setQuaternion (quaternion);
      this .addEvent ();
   },
   slerp (rotation, t)
   {
      return SFRotation .fromValue (this .getValue () .copy () .slerp (rotation .getValue (), t));
   },
   straighten (upVector)
   {
      return SFRotation .fromValue (this .getValue () .copy () .straighten (upVector ?.getValue ()));
   },
   toStream (generator)
   {
      const { x, y, z, angle } = this .getValue ();

      generator .string += generator .DoubleFormat (x);
      generator .Space ();
      generator .string += generator .DoubleFormat (y);
      generator .Space ();
      generator .string += generator .DoubleFormat (z);
      generator .Space ();
      generator .string += generator .DoubleFormat (generator .ToUnit ("angle", angle));
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      this .toStream (generator);
   },
   toJSONStream (generator)
   {
      generator .string += '[';
      generator .TidySpace ();

      this .toJSONStreamValue (generator);

      generator .TidySpace ();
      generator .string += ']';
   },
   toJSONStreamValue (generator)
   {
      const { x, y, z, angle } = this .getValue ();

      generator .string += generator .Number (generator .DoubleFormat (x));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (y));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (z));
      generator .string += ',';
      generator .TidySpace ();
      generator .string += generator .Number (generator .DoubleFormat (generator .ToUnit ("angle", angle)));
   },
});

for (const key of Object .keys (SFRotation .prototype))
   Object .defineProperty (SFRotation .prototype, key, { enumerable: false });

const SFRotation_x = {
   get ()
   {
      return this .getValue () .x;
   },
   set (value)
   {
      this .getValue () .x = +value;
      this .addEvent ();
   },
};

const SFRotation_y = {
   get ()
   {
      return this .getValue () .y;
   },
   set (value)
   {
      this .getValue () .y = +value;
      this .addEvent ();
   },
};

const SFRotation_z = {
   get ()
   {
      return this .getValue () .z;
   },
   set (value)
   {
      this .getValue () .z = +value;
      this .addEvent ();
   },
};

const SFRotation_angle = {
   get ()
   {
      return this .getValue () .angle;
   },
   set (value)
   {
      this .getValue () .angle = +value;
      this .addEvent ();
   },
};

Object .defineProperties (SFRotation .prototype,
{
   0: SFRotation_x,
   1: SFRotation_y,
   2: SFRotation_z,
   3: SFRotation_angle,
   x: Object .assign ({ enumerable: true }, SFRotation_x),
   y: Object .assign ({ enumerable: true }, SFRotation_y),
   z: Object .assign ({ enumerable: true }, SFRotation_z),
   angle: Object .assign ({ enumerable: true }, SFRotation_angle),
});

Base_X3DField .addStaticProperties (SFRotation, "SFRotation");

Object .defineProperties (SFRotation,
{
   IDENTITY:
   {
      value: SFRotation .fromValue (Numbers_Rotation4 .IDENTITY),
      enumerable: true,
   },
   fromMatrix:
   {
      value (matrix)
      {
         const rotation = new this ();

         rotation .setMatrix (matrix);

         return rotation;
      },
   },
   fromQuaternion:
   {
      value (quaternion)
      {
         const rotation = new this ();

         rotation .setQuaternion (quaternion);

         return rotation;
      },
   },
});

const SFRotation_default_ = SFRotation;
;

/* harmony default export */ const Fields_SFRotation = (Namespace/* default */.A .add ("SFRotation", SFRotation_default_));
;// ./src/x_ite/Fields/SFVec2.js




function SFVec2Template (TypeName, double)
{
   function SFVec2 (x = 0, y = 0)
   {
      Base_X3DField .call (this, new Numbers_Vector2 (+x, +y));
   }

   Fields_SFVecPrototypeTemplate (SFVec2, TypeName, Numbers_Vector2, double)

   return SFVec2;
}

const SFVec2 = {
   SFVec2d: SFVec2Template ("SFVec2d", true),
   SFVec2f: SFVec2Template ("SFVec2f", false),
};

const SFVec2_default_ = SFVec2;
;

/* harmony default export */ const Fields_SFVec2 = (Namespace/* default */.A .add ("SFVec2", SFVec2_default_));
;// ./src/x_ite/Fields/SFVec4.js




function SFVec4Template (TypeName, double)
{
   function SFVec4 (x = 0, y = 0, z = 0, w = 1)
   {
      Base_X3DField .call (this, new Numbers_Vector4 (+x, +y, +z, +w));
   }

   Fields_SFVecPrototypeTemplate (SFVec4, TypeName, Numbers_Vector4, double,
   {
      isDefaultValue ()
      {
         return this .getValue () .equals (Numbers_Vector4 .W_AXIS);
      },
   });

   return SFVec4;
}

const SFVec4 = {
   SFVec4d: SFVec4Template ("SFVec4d", true),
   SFVec4f: SFVec4Template ("SFVec4f", false),
};

const SFVec4_default_ = SFVec4;
;

/* harmony default export */ const Fields_SFVec4 = (Namespace/* default */.A .add ("SFVec4", SFVec4_default_));
;// ./src/x_ite/Base/X3DArrayField.js




function X3DArrayField (value)
{
   Base_X3DField .call (this, value);
}

Object .assign (Object .setPrototypeOf (X3DArrayField .prototype, Base_X3DField .prototype),
{
   // Implement all function also in TypedArray, if possible.
   at: Array .prototype .at,
   concat: Array .prototype .concat,
   // copyWithin: Array.prototype.copyWithin,
   entries: Array .prototype .entries,
   every: Array .prototype .every,
   fill: Array .prototype .fill,
   filter (/* callbackFn, thisArg */)
   {
      const array = new (this .constructor) ();

      for (const v of Array .prototype .filter .call (this, ... arguments))
         array .push (v);

      return array;
   },
   find: Array .prototype .find,
   findIndex: Array .prototype .findIndex,
   findLast: Array .prototype .findLast,
   findLastIndex: Array .prototype .findLastIndex,
   flat: Array .prototype .flat,
   flatMap: Array .prototype .flatMap,
   forEach: Array .prototype .forEach,
   includes: Array .prototype .includes,
   indexOf: Array .prototype .indexOf,
   join: Array .prototype .join,
   keys: Array .prototype .keys,
   lastIndexOf: Array .prototype .lastIndexOf,
   map (/* callbackFn, thisArg */)
   {
      const array = new (this .constructor) ();

      for (const v of Array .prototype .map .call (this, ... arguments))
         array .push (v);

      return array;
   },
   reduce: Array .prototype .reduce,
   reduceRight: Array .prototype .reduceRight,
   reverse: Array .prototype .reverse,
   slice (/* start, end */)
   {
      const array = new (this .constructor) ();

      for (const v of Array .prototype .slice .call (this, ... arguments))
         array .push (v);

      return array;
   },
   some: Array .prototype .some,
   sort: Array .prototype .sort,
   toReversed ()
   {
      return this .copy () .reverse ();
   },
   toSorted (/* compareFn */)
   {
      return this .copy () .sort (... arguments);
   },
   toSpliced (/* start, deleteCount, ... insertValues */)
   {
      const copy = this .copy ();

      copy .splice (... arguments);

      return copy;
   },
   values: Array .prototype .values,
   with (index, value)
   {
      const copy = this .copy ();

      copy [index] = value;

      return copy;
   },
});

for (const key of Object .keys (X3DArrayField .prototype))
   Object .defineProperty (X3DArrayField .prototype, key, { enumerable: false });

const X3DArrayField_default_ = X3DArrayField;
;

/* harmony default export */ const Base_X3DArrayField = (Namespace/* default */.A .add ("X3DArrayField", X3DArrayField_default_));
;// ./src/x_ite/Base/X3DObjectArrayField.js



const
   X3DObjectArrayField_target = Symbol (),
   X3DObjectArrayField_proxy  = Symbol (),
   _insert = Symbol (),
   _erase  = Symbol ();

const X3DObjectArrayField_handler =
{
   get (target, key, receiver)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            const array = target .getValue ();

            if (index >= array .length)
               return undefined;

            return array [index] .valueOf ();
         }
      }

      return Reflect .get (target, key, receiver);
   },
   set (target, key, value, receiver)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            const array = target .getValue ();

            if (index >= array .length)
               target .resize (index + 1);

            array [index] .setValue (value);

            return true;
         }
      }

      return Reflect .set (target, key, value, receiver);
   },
   has (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
            return index < target .getValue () .length;
      }

      return Reflect .has (target, key);
   },
   ownKeys (target)
   {
      return Object .keys (target .getValue ()) .concat (Reflect .ownKeys (target));
   },
   getOwnPropertyDescriptor (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            if (index < target .getValue () .length)
               return Object .getOwnPropertyDescriptor (target .getValue (), key);
         }
      }

      return Reflect .getOwnPropertyDescriptor (target, key);
   },
};

function X3DObjectArrayField (values)
{
   const proxy = new Proxy (this, X3DObjectArrayField_handler);

   Base_X3DArrayField .call (this, [ ]);

   this [X3DObjectArrayField_target] = this;
   this [X3DObjectArrayField_proxy]  = proxy;

   for (const value of values)
      this .push (value);

   return proxy;
}

Object .assign (Object .setPrototypeOf (X3DObjectArrayField .prototype, Base_X3DArrayField .prototype),
{
   [X3DObjectArrayField_target]: null,
   [X3DObjectArrayField_proxy]: null,
   *[Symbol .iterator] ()
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue ();

      for (const value of array)
         yield value .valueOf ();
   },
   getTarget ()
   {
      return this [X3DObjectArrayField_target];
   },
   copy ()
   {
      const
         target = this [X3DObjectArrayField_target],
         copy   = target .create ();

      copy .assign (target);

      return copy;
   },
   equals (array)
   {
      const
         target = this [X3DObjectArrayField_target],
         a      = target .getValue (),
         b      = array .getValue (),
         length = a .length;

      if (a === b)
         return true;

      if (length !== b .length)
         return false;

      for (let i = 0; i < length; ++ i)
      {
         if (!a [i] .equals (b [i]))
            return false;
      }

      return true;
   },
   isDefaultValue ()
   {
      return this .length === 0;
   },
   set (value)
   {
      const
         target    = this [X3DObjectArrayField_target],
         array     = target .getValue (),
         newLength = value .length;

      target .resize (newLength, undefined, true);

      for (let i = 0; i < newLength; ++ i)
         array [i] .set (value [i] instanceof Base_X3DField ? value [i] .getValue () : value [i]);
   },
   setValue (value)
   {
      const target = this [X3DObjectArrayField_target];

      target .set (value instanceof X3DObjectArrayField ? value .getValue () : value);
      target .addEvent ();
   },
   unshift (... args)
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue ();

      for (let i = args .length - 1; i >= 0; -- i)
      {
         const field = new (target .getSingleType ()) ();

         field .setValue (args [i]);
         target .addChildObject (field);
         array .unshift (field);
      }

      target .addEvent ();

      return array .length;
   },
   shift ()
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue ();

      if (array .length)
      {
         const
            field  = array .shift (),
            result = field .valueOf ();

         target .removeChildObject (field);
         target .addEvent ();

         return result;
      }
   },
   push (... args)
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue ();

      for (const argument of args)
      {
         const field = new (target .getSingleType ()) ();

         field .setValue (argument);
         target .addChildObject (field);
         array .push (field);
      }

      target .addEvent ();

      return array .length;
   },
   pop ()
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue ();

      if (array .length)
      {
         const
            field  = array .pop (),
            result = field .valueOf ();

         target .removeChildObject (field);
         target .addEvent ();

         return result;
      }
   },
   splice (index, deleteCount, ... insertValues)
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue (),
         length = array .length;

      if (arguments .length === 0)
         return new (target .constructor) ();

      if (arguments .length < 2)
         deleteCount = length;

      const result = target [_erase] (index, deleteCount);

      if (insertValues .length)
         target [_insert] (index, insertValues);

      return result;
   },
   [_insert] (index, array)
   {
      const
         target = this [X3DObjectArrayField_target],
         args   = [ ];

      for (const value of array)
      {
         const field = new (target .getSingleType ()) ();

         field .setValue (value);
         target .addChildObject (field);
         args .push (field);
      }

      target .getValue () .splice (index, 0, ... args);
      target .addEvent ();
   },
   [_erase] (index, deleteCount)
   {
      const
         target = this [X3DObjectArrayField_target],
         values = target .getValue () .splice (index, deleteCount),
         result = new (target .constructor) ();

      for (const value of values)
      {
         result .push (value);
         target .removeChildObject (value);
      }

      target .addEvent ();

      return result;
   },
   resize (newLength, value, silently)
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue (),
         length = array .length;

      if (newLength < 0)
         throw new RangeError ("Invalid array length");

      if (newLength < length)
      {
         for (let i = newLength; i < length; ++ i)
            target .removeChildObject (array [i]);

         array .length = newLength;

         if (!silently)
            target .addEvent ();
      }
      else if (newLength > length)
      {
         for (let i = length; i < newLength; ++ i)
         {
            const field = new (target .getSingleType ()) ();

            if (value !== undefined)
               field .setValue (value);

            target .addChildObject (field);
            array .push (field);
         }

         if (!silently)
            target .addEvent ();
      }
   },
   addChildObject (value)
   {
      value .addParent (this [X3DObjectArrayField_proxy]);
   },
   removeChildObject (value)
   {
      value .dispose ();
   },
   shrinkToFit ()
   {
      return this .getValue ();
   },
   concat (... args)
   {
      const
         result = this .copy (),
         target = result [X3DObjectArrayField_target];

      for (const arg of args)
      {
         for (const value of arg)
            target .push (value);
      }

      return result;
   },
   reverse ()
   {
      const target = this [X3DObjectArrayField_target];

      target .getValue () .reverse ();
      target .addEvent ();

      return target [X3DObjectArrayField_proxy];
   },
   sort (compareFn)
   {
      const target = this [X3DObjectArrayField_target];

      Array .prototype .sort .call (this, compareFn);
      target .addEvent ();

      return target [X3DObjectArrayField_proxy];
   },
   valueOf ()
   {
      return this [X3DObjectArrayField_proxy];
   },
   toStream (generator)
   {
      const
         target = this [X3DObjectArrayField_target],
         array  = target .getValue (),
         length = array .length;

      switch (length)
      {
         case 0:
         {
            generator .string += "[";
            generator .TidySpace ();
            generator .string += "]";
            break;
         }
         case 1:
         {
            array [0] .toStream (generator);
            break;
         }
         default:
         {
            const last = length - 1;

            generator .string += "[";
            generator .ListStart ();
            generator .IncIndent ();

            for (let i = 0; i < last; ++ i)
            {
               generator .ListIndent ();
               array [i] .toStream (generator);
               generator .Comma ();
               generator .ListBreak ();
            }

            generator .ListIndent ();
            array .at (-1) .toStream (generator);

            generator .ListEnd ();
            generator .DecIndent ();
            generator .ListIndent ();
            generator .string += "]";
            break;
         }
      }
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      const
         target = this [X3DObjectArrayField_target],
         length = target .length;

      if (length)
      {
         const
            array = target .getValue (),
            last  = length - 1;

         for (let i = 0; i < last; ++ i)
         {
            array [i] .toXMLStream (generator);
            generator .Comma ();
            generator .TidySpace ();
         }

         array .at (-1) .toXMLStream (generator);
      }
   },
   toJSONStream (generator)
   {
      const
         target = this [X3DObjectArrayField_target],
         length = target .length;

      if (length)
      {
         const
            array = target .getValue (),
            last  = length - 1;

         generator .string += '[';
         generator .ListBreak ();
         generator .IncIndent ();

         for (let i = 0; i < last; ++ i)
         {
            generator .ListIndent ();

            array [i] .toJSONStreamValue (generator);

            generator .string += ',';
            generator .ListBreak ();
         }

         generator .ListIndent ();

         array .at (-1) .toJSONStreamValue (generator);

         generator .ListBreak ();
         generator .DecIndent ();
         generator .ListIndent ();
         generator .string += ']';
      }
      else
      {
         generator .string += '[';
         generator .TidySpace ();
         generator .string += ']';
      }
   },
});

for (const key of Object .keys (X3DObjectArrayField .prototype))
   Object .defineProperty (X3DObjectArrayField .prototype, key, { enumerable: false });

Object .defineProperty (X3DObjectArrayField .prototype, "length",
{
   get () { return this [X3DObjectArrayField_target] .getValue () .length; },
   set (value) { this [X3DObjectArrayField_target] .resize (value); },
});

const X3DObjectArrayField_default_ = X3DObjectArrayField;
;

/* harmony default export */ const Base_X3DObjectArrayField = (Namespace/* default */.A .add ("X3DObjectArrayField", X3DObjectArrayField_default_));
;// ./src/x_ite/Base/X3DTypedArrayField.js



const
   X3DTypedArrayField_target = Symbol (),
   X3DTypedArrayField_proxy  = Symbol (),
   _cache  = Symbol (),
   _tmp    = Symbol (),
   _length = Symbol (),
   X3DTypedArrayField_insert = Symbol (),
   X3DTypedArrayField_erase  = Symbol (),
   _grow   = Symbol (),
   _fill   = Symbol ();

const X3DTypedArrayField_handler =
{
   get (target, key, receiver)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            if (index >= target [_length])
               return undefined;

            const
               components = target .getComponents (),
               valueType  = target .getValueType (),
               array      = target .getValue ();

            if (components === 1)
            {
               // Return native JavaScript value.
               return valueType (array [index]);
            }
            else
            {
               // Return reference to index.

               return target [_cache] [index] ??= createValue (target, index, components, valueType);
            }
         }
      }

      return Reflect .get (target, key, receiver);
   },
   set (target, key, value, receiver)
   {
      if (typeof key === "string")
      {
         let index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            const components = target .getComponents ();

            let array = target .getValue ();

            if (index >= target [_length])
               array = target .resize (index + 1, target .getSingleValue ());

            if (components === 1)
            {
               const valueType = target .getValueType ();

               array [index] = valueType (value);
            }
            else
            {
               index *= components;

               for (let c = 0; c < components; ++ c, ++ index)
                  array [index] = value [c];
            }

            target .addEvent ();

            return true;
         }
      }

      return Reflect .set (target, key, value, receiver);
   },
   has (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
            return index < target [_length];
      }

      return Reflect .has (target, key);
   },
   ownKeys (target)
   {
      const
         length  = target [_length],
         ownKeys = [ ];

      for (let i = 0; i < length; ++ i)
         ownKeys .push (String (i));

      return ownKeys .concat (Reflect .ownKeys (target));
   },
   getOwnPropertyDescriptor (target, key)
   {
      if (typeof key === "string")
      {
         const index = +key;

         if (Number .isInteger (index) && index >= 0)
         {
            if (index < target [_length])
               return Object .getOwnPropertyDescriptor (target .getValue (), key);
         }
      }

      return Reflect .getOwnPropertyDescriptor (target, key);
   },
};

function X3DTypedArrayField (values)
{
   const proxy = new Proxy (this, X3DTypedArrayField_handler);

   Base_X3DArrayField .call (this, new (this .getArrayType ()) (16));

   this [X3DTypedArrayField_target] = this;
   this [X3DTypedArrayField_proxy]  = proxy;

   if (this .getComponents () > 1)
   {
      this [_cache] = [ ]; // Cache of elements.
      this [_tmp]   = [ ]; // Array with components size.
   }

   for (const value of values)
      this .push (value);

   return proxy;
}

Object .assign (Object .setPrototypeOf (X3DTypedArrayField .prototype, Base_X3DArrayField .prototype),
{
   [X3DTypedArrayField_target]: null,
   [_tmp]: null,
   [_length]: 0,
   *[Symbol .iterator] ()
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .getValue (),
         components = target .getComponents (),
         valueType  = target .getValueType (),
         length     = target [_length];

      if (components === 1)
      {
         // Return native JavaScript value.

         for (let index = 0; index < length; ++ index)
            yield valueType (array [index]);
      }
      else
      {
         // Return reference to index.

         const cache = target [_cache];

         for (let index = 0; index < length; ++ index)
         {
            yield cache [index]
               ?? (cache [index] = createValue (target, index, components, valueType));
         }
      }
   },
   getTarget ()
   {
      return this [X3DTypedArrayField_target];
   },
   copy ()
   {
      const
         target = this [X3DTypedArrayField_target],
         array  = target .getValue (),
         copy   = target .create ();

      copy .set (array, target [_length]);

      return copy;
   },
   equals (other)
   {
      const
         target      = this [X3DTypedArrayField_target],
         otherTarget = other [X3DTypedArrayField_target],
         length      = target [_length];

      if (target === otherTarget)
         return true;

      if (length !== otherTarget [_length])
         return false;

      const
         a = target .getValue (),
         b = otherTarget .getValue (),
         l = length * target .getComponents ();

      for (let i = 0; i < l; ++ i)
      {
         if (a [i] !== b [i])
            return false;
      }

      return true;
   },
   assign (value)
   {
      const target = this [X3DTypedArrayField_target];

      target .set (value .getValue (), value .length);
      target .addEvent ();
   },
   set (otherArray /* value of field */, l /* length of field */)
   {
      const
         target      = this [X3DTypedArrayField_target],
         components  = target .getComponents (),
         length      = target [_length];

      let
         array       = target .getValue (),
         otherLength = l !== undefined ? l * components : otherArray .length;

      const rest = otherLength % components;

      if (rest)
      {
         otherLength -= rest;

         console .warn (`Array length must be multiple of components size, which is ${components}.`);
      }

      otherLength /= components;

      if (array .length < otherArray .length)
      {
         array = target [_grow] (otherArray .length);

         array .set (otherArray);

         if (rest)
            array .fill (0, otherLength * components, otherLength * components + rest);
      }
      else
      {
         array .set (otherArray);

         if (otherLength < length)
            array .fill (0, otherLength * components, length * components);
      }

      target [_length] = otherLength;
   },
   isDefaultValue ()
   {
      return this [_length] === 0;
   },
   setValue (value)
   {
      const target = this [X3DTypedArrayField_target];

      if (value instanceof target .constructor)
      {
         target .assign (value);
      }
      else
      {
         target .set (value);
         target .addEvent ();
      }
   },
   unshift (... args)
   {
      const
         target          = this [X3DTypedArrayField_target],
         components      = target .getComponents (),
         length          = target [_length],
         argumentsLength = args .length,
         array           = target [_grow] ((length + argumentsLength) * components);

      array .copyWithin (argumentsLength * components, 0, length * components);

      if (components === 1)
      {
         const valueType = target .getValueType ();

         for (let a = 0; a < argumentsLength; ++ a)
            array [a] = valueType (args [a]);
      }
      else
      {
         for (let i = 0, a = 0; a < argumentsLength; ++ a)
         {
            const argument = args [a];

            for (let c = 0; c < components; ++ c, ++ i)
            {
               array [i] = argument [c];
            }
         }
      }

      target [_length] += argumentsLength;

      target .addEvent ();

      return target [_length];
   },
   shift ()
   {
      const
         target = this [X3DTypedArrayField_target],
         length = target [_length];

      if (length)
      {
         const
            array      = target .getValue (),
            components = target .getComponents (),
            valueType  = target .getValueType (),
            newLength  = length - 1;

         let value;

         if (components === 1)
         {
            value = valueType (array [0]);
         }
         else
         {
            const tmp = target [_tmp];

            for (let c = 0; c < components; ++ c)
               tmp [c] = array [c];

            value = new valueType (... tmp);
         }

         array .copyWithin (0, components, length * components);
         array .fill (0, components * newLength, length * components);

         target [_length] = newLength;

         target .addEvent ();
         return value;
      }
   },
   push (... args)
   {
      const
         target          = this [X3DTypedArrayField_target],
         components      = target .getComponents (),
         length          = target [_length],
         argumentsLength = args .length,
         array           = target [_grow] ((length + argumentsLength) * components);

      if (components === 1)
      {
         const valueType = target .getValueType ();

         for (let a = 0, i = length; a < argumentsLength; ++ a, ++ i)
            array [i] = valueType (args [a]);
      }
      else
      {
         for (let i = length * components, a = 0; a < argumentsLength; ++ a)
         {
            const argument = args [a];

            for (let c = 0; c < components; ++ c,  ++ i)
            {
               array [i] = argument [c];
            }
         }
      }

      target [_length] += argumentsLength;

      target .addEvent ();

      return target [_length];
   },
   pop ()
   {
      const
         target = this [X3DTypedArrayField_target],
         length = target [_length];

      if (length)
      {
         const
            array      = target .getValue (),
            components = target .getComponents (),
            valueType  = target .getValueType (),
            newLength  = length - 1;

         let value;

         if (components === 1)
         {
            value = valueType (array [length - 1]); // Don't use at(-1).
         }
         else
         {
            const tmp = target [_tmp];

            for (let c = 0, a = newLength * components; c < components; ++ c, ++ a)
               tmp [c] = array [a];

            value = new valueType (... tmp);
         }

         array .fill (0, newLength * components, length * components);

         target [_length] = newLength;

         target .addEvent ();

         return value;
      }
   },
   splice (index, deleteCount, ... insertValues)
   {
      const
         target = this [X3DTypedArrayField_target],
         length = target [_length];

      if (arguments .length === 0)
         return new (target .constructor) ();

      index |= 0;

      if (index < 0)
         index += length;

      index = Math_Algorithm .clamp (index, 0, length);

      if (arguments .length < 2)
         deleteCount = length;

      deleteCount |= 0;

      if (index + deleteCount > length)
         deleteCount = length - index;

      deleteCount = Math .max (deleteCount, 0);

      const result = target [X3DTypedArrayField_erase] (index, index + deleteCount);

      if (insertValues .length)
         target [X3DTypedArrayField_insert] (index, insertValues);

      target .addEvent ();

      return result;
   },
   [X3DTypedArrayField_insert] (index, other)
   {
      const
         target      = this [X3DTypedArrayField_target],
         components  = target .getComponents (),
         length      = target [_length],
         otherLength = other .length,
         array       = target [_grow] ((length + otherLength) * components);

      index *= components;

      array .copyWithin (index + otherLength * components, index, length * components);

      if (components === 1)
      {
         const valueType = target .getValueType ();

         for (let a = 0, i = index; a < otherLength; ++ a, ++ i)
            array [i] = valueType (other [a]);
      }
      else
      {
         for (let i = 0, a = index; i < otherLength; ++ i)
         {
            const value = other [i];

            for (let c = 0; c < components; ++ c, ++ a)
               array [a] = value [c];
         }
      }

      target [_length] += otherLength;
   },
   [X3DTypedArrayField_erase] (first, last)
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .getValue (),
         components = target .getComponents (),
         difference = last - first,
         length     = target [_length],
         newLength  = length - difference,
         values     = target [X3DTypedArrayField_proxy] .slice (first, last);

      first *= components;
      last  *= components;

      array .copyWithin (first, last, length * components);
      array .fill (0, newLength * components, length * components);

      target [_length] = newLength;

      if (components > 1)
         target [_cache] .length = newLength;

      target .addEvent ();

      return values;
   },
   resize (newLength, value, silently)
   {
      const
         target     = this [X3DTypedArrayField_target],
         length     = target [_length],
         components = target .getComponents ();

      if (newLength < 0)
         throw new RangeError ("Invalid array length");

      let array = target .getValue ();

      target [_length] = newLength;

      if (newLength < length)
      {
         array .fill (0, newLength * components, length * components);

         if (components > 1)
            target [_cache] .length = newLength;

         if (!silently)
            target .addEvent ();
      }
      else if (newLength > length)
      {
         array = target [_grow] (newLength * components);

         if (value !== undefined)
            this [_fill] (value, length, newLength);

         if (!silently)
            target .addEvent ();
      }

      return array;
   },
   [_grow] (length)
   {
      const
         target = this [X3DTypedArrayField_target],
         array  = target .getValue ();

      if (length <= array .length)
         return array;

      const
         maxLength = Math_Algorithm .nextPowerOfTwo (length),
         newArray  = new (target .getArrayType ()) (maxLength);

      newArray .set (array);

      Base_X3DArrayField .prototype .set .call (target, newArray);

      return newArray;
   },
   shrinkToFit ()
   {
      const
         target = this [X3DTypedArrayField_target],
         array  = target .getValue (),
         length = target [_length] * target .getComponents ();

      if (array .length === length)
         return array;

      const newArray = array .subarray (0, length);

      Base_X3DArrayField .prototype .set .call (target, newArray);

      return newArray;
   },
   concat (... args)
   {
      const
         result     = this .copy (),
         target     = result [X3DTypedArrayField_target],
         components = target .getComponents (),
         length     = target [_length] + args .reduce ((p, c) => p + c .length, 0),
         value      = target [_grow] (length * components);

      let offset = target [_length] * components;

      for (const arg of args)
      {
         value .set (arg .shrinkToFit (), offset);

         offset += arg .length * components;
      }

      target [_length] = length;

      return result;
   },
   flat ()
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .shrinkToFit (),
         components = target .getComponents (),
         valueType  = target .getValueType ();

      if (components === 1)
         return Array .from (array, value => valueType (value));

      return Array .from (array);
   },
   flatMap (... args)
   {
      return this .map (...args) .flat ();
   },
   [_fill] (value, start = 0, end = this .length)
   {
      const
         target     = this [X3DTypedArrayField_target],
         length     = target [_length],
         array      = target .getValue (),
         components = target .getComponents ();

      if (-length <= start && start < 0)
         start = start + length;

      if (start < -length)
         start = 0;

      if (start >= length)
         return;

      if (-length <= end && end < 0)
         end = end + length;

      if (end < -length)
         end = 0;

      if (end >= length)
         end = length;

      if (start >= end)
         return;

      if (components === 1)
      {
         const valueType = target .getValueType ();

         array .fill (valueType (value), start * components, end * components);
      }
      else
      {
         // More efficient way to copy repeating sequence into TypedArray?
         // https://stackoverflow.com/questions/46313130/more-efficient-way-to-copy-repeating-sequence-into-typedarray

         const
            i0 = start * components,
            il = end * components;

         for (let i = i0, c = 0; c < components; ++ i, ++ c)
         {
            array [i] = value [c];
         }

         let
            i = i0 + components,
            c = components;

         while (i < il)
         {
            const sl = i + c > il ? il - i : c;

            array .copyWithin (i, i0, i0 + sl);

            i  += c;
            c <<= 1;
         }
      }

      return target [X3DTypedArrayField_proxy];
   },
   fill (value, start = 0, end = this .length)
   {
      const target = this [X3DTypedArrayField_target];

      this [_fill] (value, start, end);

      target .addEvent ();

      return target [X3DTypedArrayField_proxy];
   },
   reverse ()
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .getValue (),
         components = target .getComponents (),
         length     = target [_length] * components,
         length1_2  = Math .floor (target [_length] / 2) * components;

      if (components === 1)
      {
         for (let i = 0; i < length1_2; ++ i)
         {
            const
               i2 = length - i - 1,
               t  = array [i];

            array [i]  = array [i2];
            array [i2] = t;
         }
      }
      else
      {
         for (let i = 0; i < length1_2; i += components)
         {
            for (let c = 0; c < components; ++ c)
            {
               const
                  i1 = i + c,
                  i2 = length - i - 1 - (components - c - 1),
                  t  = array [i1];

               array [i1] = array [i2];
               array [i2] = t;
            }
         }
      }

      target .addEvent ();

      return target [X3DTypedArrayField_proxy];
   },
   sort (compareFn)
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .getValue (),
         components = target .getComponents (),
         length     = target [_length];

      if (components === 1)
      {
         const valueType = target .getValueType ();

         const cmp = compareFn
            ? (a, b) => compareFn (valueType (a), valueType (b))
            : Math_Algorithm .cmp;

         target .set (array .subarray (0, length) .sort (cmp));
      }
      else
      {
         const result = Array .from (target [X3DTypedArrayField_proxy], value => value .copy ())
            .sort (compareFn ?? ((a, b) =>
         {
            for (let c = 0; c < components; ++ c)
            {
               if (a [c] < b [c])
                  return -1;

               if (b [c] < a [c])
                  return 1;
            }

            return 0;
         }));

         for (let i = 0; i < length; ++ i)
         {
            const value = result [i];

            for (let c = 0, first = i * components; c < components; ++ c, ++ first)
               array [first] = value [c];
         }
      }

      target .addEvent ();

      return target [X3DTypedArrayField_proxy];
   },
   valueOf ()
   {
      return this [X3DTypedArrayField_proxy];
   },
   toStream (generator)
   {
      const
         target     = this [X3DTypedArrayField_target],
         array      = target .getValue (),
         length     = target [_length],
         components = target .getComponents (),
         value      = new (target .getSingleType ()) ();

      value .setUnit (target .getUnit ());

      switch (length)
      {
         case 0:
         {
            generator .string += "[";
            generator .TidySpace ();
            generator .string += "]";
            break;
         }
         case 1:
         {
            if (components === 1)
            {
               value .set (array [0]);
               value .toStream (generator);
            }
            else
            {
               for (let c = 0, first = 0; c < components; ++ c, ++ first)
                  value [c] = array [first];

               value .toStream (generator);
            }

            break;
         }
         default:
         {
            const last = length - 1;

            generator .string += "[";
            generator .ListStart ();
            generator .IncIndent ();

            if (components === 1)
            {
               for (let i = 0; i < last; ++ i)
               {
                  generator .ListIndent ();

                  value .set (array [i * components]);
                  value .toStream (generator);

                  generator .Comma ();
                  generator .ListBreak ();
               }

               generator .ListIndent ();

               value .set (array [last * components]);
               value .toStream (generator);
            }
            else
            {
               for (let i = 0; i < last; ++ i)
               {
                  generator .ListIndent ();

                  for (let c = 0, first = i * components; c < components; ++ c, ++ first)
                     value [c] = array [first];

                  value .toStream (generator);

                  generator .Comma ();
                  generator .ListBreak ();
               }

               generator .ListIndent ();

               for (let c = 0, first = last * components; c < components; ++ c, ++ first)
                  value [c] = array [first];

               value .toStream (generator);
            }

            generator .ListEnd ();
            generator .DecIndent ();
            generator .ListIndent ();
            generator .string += "]";
            break;
         }
      }
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      const
         target = this [X3DTypedArrayField_target],
         length = target [_length];

      if (length)
      {
         const
            array      = target .getValue (),
            components = target .getComponents (),
            value      = new (target .getSingleType ()) (),
            last       = length - 1;

         value .setUnit (target .getUnit ());

         if (components === 1)
         {
            for (let i = 0; i < last; ++ i)
            {
               value .set (array [i * components]);
               value .toXMLStream (generator);

               generator .Comma ();
               generator .TidySpace ();
            }

            value .set (array [last * components]);
            value .toXMLStream (generator);
         }
         else
         {
            for (let i = 0; i < last; ++ i)
            {
               for (let c = 0, first = i * components; c < components; ++ c, ++ first)
                  value [c] = array [first];

               value .toXMLStream (generator);

               generator .Comma ();
               generator .TidySpace ();
            }

            for (let c = 0, first = last * components; c < components; ++ c, ++ first)
               value [c] = array [first];

            value .toXMLStream (generator);
         }
      }
   },
   toJSONStream (generator)
   {
      const
         target = this [X3DTypedArrayField_target],
         length = target .length;

      if (length)
      {
         const
            array      = target .getValue (),
            components = target .getComponents (),
            value      = new (target .getSingleType ()) (),
            last       = length - 1;

         value .setUnit (target .getUnit ());

         generator .string += '[';
         generator .ListBreak ();
         generator .IncIndent ();

         if (components === 1)
         {
            for (let i = 0; i < last; ++ i)
            {
               generator .ListIndent ();

               value .set (array [i * components]);
               value .toJSONStreamValue (generator);

               generator .string += ',';
               generator .ListBreak ();
            }

            generator .ListIndent ();

            value .set (array [last * components]);
            value .toJSONStreamValue (generator);
         }
         else
         {
            for (let i = 0; i < last; ++ i)
            {
               generator .ListIndent ();

               for (let c = 0, first = i * components; c < components; ++ c, ++ first)
                  value [c] = array [first];

               value .toJSONStreamValue (generator);

               generator .string += ',';
               generator .ListBreak ();
            }

            generator .ListIndent ();

            for (let c = 0, first = last * components; c < components; ++ c, ++ first)
               value [c] = array [first];

            value .toJSONStreamValue (generator);
         }

         generator .ListBreak ();
         generator .DecIndent ();
         generator .ListIndent ();
         generator .string += ']';
      }
      else
      {
         generator .string += '[';
         generator .TidySpace ();
         generator .string += ']';
      }
   },
   dispose ()
   {
      Base_X3DArrayField .prototype .dispose .call (this [X3DTypedArrayField_target]);
   },
});

for (const key of Object .keys (X3DTypedArrayField .prototype))
   Object .defineProperty (X3DTypedArrayField .prototype, key, { enumerable: false });

Object .defineProperty (X3DTypedArrayField .prototype, "length",
{
   get () { return this [_length]; },
   set (value)
   {
      const target = this [X3DTypedArrayField_target];

      target .resize (value, target .getSingleValue ());
   },
});

// Getter/Setter functions to reference a value for a given index.

function createValue (target, index, components, valueType)
{
   const
      value         = new valueType (),
      internalValue = value .getValue (),
      i             = index * components;

   Object .defineProperties (value,
   {
      addEvent:
      {
         value: addEvent .bind (target, i, components, internalValue),
         configurable: true,
      },
      getValue:
      {
         value: getValue .bind (target, i, components, internalValue),
         configurable: true,
      },
   });

   return value;
}

function getValue (index, components, internalValue)
{
   const array = this .getValue ();

   for (let c = 0; c < components; ++ c, ++ index)
      internalValue [c] = array [index];

   return internalValue;
}

function addEvent (index, components, internalValue)
{
   const array = this .getValue ();

   for (let c = 0; c < components; ++ c, ++ index)
      array [index] = internalValue [c];

   this .addEvent ();
}

const X3DTypedArrayField_default_ = X3DTypedArrayField;
;

/* harmony default export */ const Base_X3DTypedArrayField = (Namespace/* default */.A .add ("X3DTypedArrayField", X3DTypedArrayField_default_));
;// ./src/x_ite/Fields/ArrayFields.js





















const
   { SFBool, SFDouble, SFFloat, SFInt32, SFString, SFTime } = Fields_SFScalar,
   { SFMatrix3d, SFMatrix3f: ArrayFields_SFMatrix3f } = Fields_SFMatrix3,
   { SFMatrix4d, SFMatrix4f } = Fields_SFMatrix4,
   { SFVec2d, SFVec2f }       = Fields_SFVec2,
   { SFVec3d: ArrayFields_SFVec3d, SFVec3f: ArrayFields_SFVec3f }       = Fields_SFVec3,
   { SFVec4d, SFVec4f }       = Fields_SFVec4;

/*
 *  MFNode
 */

function MFNode (... args)
{
   return Base_X3DObjectArrayField .call (this, args);
}

Object .assign (Object .setPrototypeOf (MFNode .prototype, Base_X3DObjectArrayField .prototype),
{
   getSingleType ()
   {
      return Fields_SFNode;
   },
   copy (instance)
   {
      if (instance)
      {
         const copy = new MFNode ();

         for (const node of this .getValue ())
            copy .push (node .copy (instance));

         return copy;
      }
      else
      {
         return Base_X3DObjectArrayField .prototype .copy .call (this);
      }
   },
   toStream (generator)
   {
      const
         target = this .getTarget (),
         array  = target .getValue (),
         length = array .length;

      switch (length)
      {
         case 0:
         {
            generator .string += "[";
            generator .TidySpace ();
            generator .string += "]";
            break;
         }
         case 1:
         {
            array [0] .toStream (generator);
            break;
         }
         default:
         {
            generator .string += "[";
            generator .TidyBreak ();
            generator .IncIndent ();

            for (let i = 0; i < length; ++ i)
            {
               generator .Indent ();
               array [i] .toStream (generator);
               generator .TidyBreak ();
            }

            generator .DecIndent ();
            generator .Indent ();
            generator .string += "]";
            break;
         }
      }
   },
   toVRMLStream (generator)
   {
      const
         target = this .getTarget (),
         array  = target .getValue (),
         length = array .length;

      switch (length)
      {
         case 0:
         {
            generator .string += "[";
            generator .TidySpace ();
            generator .string += "]";
            break;
         }
         case 1:
         {
            array [0] .toVRMLStream (generator);
            break;
         }
         default:
         {
            generator .string += "[";
            generator .TidyBreak ();
            generator .IncIndent ();

            for (const element of array)
            {
               generator .Indent ();
               element .toVRMLStream (generator);
               generator .TidyBreak ();
            }

            generator .DecIndent ();
            generator .Indent ();
            generator .string += "]";
            break;
         }
      }
   },
   toXMLStream (generator)
   {
      const
         target = this .getTarget (),
         length = target .length;

      if (length)
      {
         const
            array = target .getValue (),
            last  = length - 1;

         for (let i = 0; i < last; ++ i)
         {
            const node = array [i] .getValue ();

            if (node)
            {
               node .toXMLStream (generator);
            }
            else
            {
               generator .openTag ("NULL");
               generator .containerField ();
               generator .closeTag ("NULL");
            }

            generator .TidyBreak ();
         }

         const node = array .at (-1) .getValue ();

         if (node)
         {
            node .toXMLStream (generator);
         }
         else
         {
            generator .openTag ("NULL");
            generator .containerField ();
            generator .closeTag ("NULL");
         }
      }
   },
   toJSONStream (generator)
   {
      const
         target = this .getTarget (),
         length = target .length;

      if (length)
      {
         const
            array = target .getValue (),
            last  = length - 1;

         generator .string += '[';
         generator .TidyBreak ();
         generator .IncIndent ();

         for (let i = 0; i < last; ++ i)
         {
            generator .Indent ();

            if (array [i])
               array [i] .toJSONStreamValue (generator);
            else
               generator .string += 'null';

            generator .string += ',';
            generator .TidyBreak ();
         }

         generator .Indent ();

         if (array .at (-1))
            array .at (-1) .toJSONStreamValue (generator);
         else
            generator .string += 'null';

         generator .TidyBreak ();
         generator .DecIndent ();
         generator .Indent ();
         generator .string += ']';
      }
      else
      {
         generator .string += '[';
         generator .TidySpace ();
         generator .string += ']';
      }
   },
   dispose ()
   {
      const target = this .getTarget ();

      target .resize (0, undefined, true);
      target .processInterests ();

      Base_X3DObjectArrayField .prototype .dispose .call (target);
   },
});

for (const key of Object .keys (MFNode .prototype))
   Object .defineProperty (MFNode .prototype, key, { enumerable: false });

Base_X3DField .addStaticProperties (MFNode, "MFNode");

function MFString (... args)
{
   return Base_X3DObjectArrayField .call (this, args);
}

Object .assign (Object .setPrototypeOf (MFString .prototype, Base_X3DObjectArrayField .prototype),
{
   getSingleType ()
   {
      return SFString;
   },
   toXMLStream (generator, sourceText = false)
   {
      const
         target = this .getTarget (),
         length = target .length;

      if (length)
      {
         const value = target .getValue ();

         for (let i = 0, n = length - 1; i < n; ++ i)
         {
            generator .string += "\"";
            value [i] .toXMLStream (generator, sourceText);
            generator .string += "\"";
            generator .Comma ();
            generator .TidySpace ();
         }

         generator .string += "\"";
         value .at (-1) .toXMLStream (generator, sourceText);
         generator .string += "\"";
      }
   },
});

for (const key of Object .keys (MFString .prototype))
   Object .defineProperty (MFString .prototype, key, { enumerable: false });

Base_X3DField .addStaticProperties (MFString, "MFString");

/**
 * MFImage
 */

function MFImage (... args)
{
   return Base_X3DObjectArrayField .call (this, args);
}

Object .assign (Object .setPrototypeOf (MFImage .prototype, Base_X3DObjectArrayField .prototype),
{
   getSingleType ()
   {
      return Fields_SFImage;
   },
});

for (const key of Object .keys (MFImage .prototype))
   Object .defineProperty (MFImage .prototype, key, { enumerable: false });

Base_X3DField .addStaticProperties (MFImage, "MFImage");

function TypedArrayTemplate (TypeName, SingleType, ValueType, ArrayType, Components, singleValue)
{
   function ArrayField (... args)
   {
      return Base_X3DTypedArrayField .call (this, args);
   }

   Object .assign (Object .setPrototypeOf (ArrayField .prototype, Base_X3DTypedArrayField .prototype),
   {
      getSingleValue ()
      {
         return singleValue;
      },
      getSingleType ()
      {
         return SingleType;
      },
      getValueType ()
      {
         return ValueType;
      },
      getArrayType ()
      {
         return ArrayType;
      },
      getComponents ()
      {
         return Components;
      },
   });

   for (const key of Object .keys (ArrayField .prototype))
      Object .defineProperty (ArrayField .prototype, key, { enumerable: false });

   Base_X3DField .addStaticProperties (ArrayField, TypeName);

   return ArrayField;
}

const Value = value => value;

const ArrayFields =
{
   MFBool:       TypedArrayTemplate ("MFBool",       SFBool,       Boolean,      Uint8Array,   1),
   MFColor:      TypedArrayTemplate ("MFColor",      Fields_SFColor,      Fields_SFColor,      Float32Array, 3),
   MFColorRGBA:  TypedArrayTemplate ("MFColorRGBA",  Fields_SFColorRGBA,  Fields_SFColorRGBA,  Float32Array, 4),
   MFDouble:     TypedArrayTemplate ("MFDouble",     SFDouble,     Value,        Float64Array, 1),
   MFFloat:      TypedArrayTemplate ("MFFloat",      SFFloat,      Value,        Float32Array, 1),
   MFImage,
   MFInt32:      TypedArrayTemplate ("MFInt32",      SFInt32,      Value,        Int32Array,   1),
   MFMatrix3d:   TypedArrayTemplate ("MFMatrix3d",   SFMatrix3d,   SFMatrix3d,   Float64Array, 9,  Numbers_Matrix3 .IDENTITY),
   MFMatrix3f:   TypedArrayTemplate ("MFMatrix3f",   ArrayFields_SFMatrix3f,   ArrayFields_SFMatrix3f,   Float32Array, 9,  Numbers_Matrix3 .IDENTITY),
   MFMatrix4d:   TypedArrayTemplate ("MFMatrix4d",   SFMatrix4d,   SFMatrix4d,   Float64Array, 16, Numbers_Matrix4 .IDENTITY),
   MFMatrix4f:   TypedArrayTemplate ("MFMatrix4f",   SFMatrix4f,   SFMatrix4f,   Float32Array, 16, Numbers_Matrix4 .IDENTITY),
   MFNode,
   MFQuaternion: TypedArrayTemplate ("MFQuaternion", Fields_SFQuaternion, Fields_SFQuaternion, Float64Array, 4,  Numbers_Quaternion .ZERO),
   MFRotation:   TypedArrayTemplate ("MFRotation",   Fields_SFRotation,   Fields_SFRotation,   Float64Array, 4,  Numbers_Rotation4  .IDENTITY),
   MFString,
   MFTime:       TypedArrayTemplate ("MFTime",       SFTime,       Value,        Float64Array, 1,  -1),
   MFVec2d:      TypedArrayTemplate ("MFVec2d",      SFVec2d,      SFVec2d,      Float64Array, 2),
   MFVec2f:      TypedArrayTemplate ("MFVec2f",      SFVec2f,      SFVec2f,      Float32Array, 2),
   MFVec3d:      TypedArrayTemplate ("MFVec3d",      ArrayFields_SFVec3d,      ArrayFields_SFVec3d,      Float64Array, 3),
   MFVec3f:      TypedArrayTemplate ("MFVec3f",      ArrayFields_SFVec3f,      ArrayFields_SFVec3f,      Float32Array, 3),
   MFVec4d:      TypedArrayTemplate ("MFVec4d",      SFVec4d,      SFVec4d,      Float64Array, 4,  Numbers_Vector4 .W_AXIS),
   MFVec4f:      TypedArrayTemplate ("MFVec4f",      SFVec4f,      SFVec4f,      Float32Array, 4,  Numbers_Vector4 .W_AXIS),
};

const ArrayFields_default_ = ArrayFields;
;

/* harmony default export */ const Fields_ArrayFields = (Namespace/* default */.A .add ("ArrayFields", ArrayFields_default_));
;// ./src/x_ite/Fields/SFImage.js




const { MFInt32 } = Fields_ArrayFields;

/*
 *  Image
 */

function Image (width, height, comp, array)
{
   this .width  = Math .max (width|0, 0);
   this .height = Math .max (height|0, 0);
   this .comp   = Math_Algorithm .clamp (comp|0, 0, 4);
   this .array  = new MFInt32 ();

   if (array)
      this .array .setValue (array);

   this .array .length = this .width * this .height;
}

Object .assign (Image .prototype,
{
   equals (image)
   {
      return this .width  === image .width &&
             this .height === image .height &&
             this .comp   === image .comp &&
             this .array .equals (image .array);
   },
   assign (image)
   {
      this .width  = image .width;
      this .height = image .height;
      this .comp   = image .comp;

      this .array .assign (image .array);
   },
   getWidth ()
   {
      return this .width;
   },
   setWidth (value)
   {
      this .width = Math .max (value|0, 0);
   },
   getHeight ()
   {
      return this .height;
   },
   setHeight (value)
   {
      this .height = Math .max (value|0, 0);
   },
   getComp ()
   {
      return this .comp;
   },
   setComp (value)
   {
      this .comp = Math_Algorithm .clamp (value|0, 0, 4);
   },
   getArray ()
   {
      return this .array;
   },
   setArray (value)
   {
      this .array .setValue (value);
   },
});

/*
 *  SFImage
 */

function SFImage (width = 0, height = 0, comp = 0, array)
{
   Base_X3DField .call (this, new Image (width, height, comp, array));

   this .getValue () .getArray () .addParent (this);
}

Object .assign (Object .setPrototypeOf (SFImage .prototype, Base_X3DField .prototype),
{
   *[Symbol .iterator] ()
   {
      yield  this .width;
      yield  this .height;
      yield  this .comp;
      yield* this .array;
   },
   addEvent ()
   {
      const size = this .width * this .height;

      if (this .array .length !== size)
         this .array .length = size;

      Base_X3DField .prototype .addEvent .call (this);
   },
   copy ()
   {
      return new SFImage (this .width, this .height, this .comp, this .array);
   },
   equals (image)
   {
      return this .getValue () .equals (image .getValue ());
   },
   isDefaultValue ()
   {
      return (
         this .width  === 0 &&
         this .height === 0 &&
         this .comp   === 0);
   },
   set (image)
   {
      this .getValue () .assign (image);
   },
   toStream (generator)
   {
      const
         width  = this .width,
         height = this .height,
         array  = new Uint32Array (this .array .getValue () .buffer);

      generator .string += width;
      generator .Space ();
      generator .string += height;
      generator .Space ();
      generator .string += this .comp;
      generator .AttribBreak ();

      generator .IncIndent ();

      for (let y = 0; y < height; ++ y)
      {
         generator .ListIndent ();

         const s = y * width;

         for (let x = 0; x < width; ++ x)
         {
            generator .string += "0x";
            generator .string += array [x + s] .toString (16);

            if (x !== width - 1)
               generator .Space ();
         }

         if (y !== height - 1)
            generator .AttribBreak ();
      }

      generator .DecIndent ();
   },
   toVRMLStream (generator)
   {
      this .toStream (generator);
   },
   toXMLStream (generator)
   {
      this .toStream (generator);
   },
   toJSONStream (generator)
   {
      generator .string += '[';
      generator .ListBreak ();
      generator .IncIndent ();
      generator .ListIndent ();

      this .toJSONStreamValue (generator);

      generator .DecIndent ();
      generator .ListBreak ();
      generator .ListIndent ();
      generator .string += ']';
   },
   toJSONStreamValue (generator)
   {
      const
         width  = this .width,
         height = this .height,
         array  = new Uint32Array (this .array .getValue () .buffer),
         length = this .array .length;

      generator .string += width;
      generator .string += ',';
      generator .TidySpace ();
      generator .string += height;
      generator .string += ',';
      generator .TidySpace ();
      generator .string += this .comp;
      generator .string += ',';

      if (width && height)
      {
         generator .ListBreak ();
         generator .IncIndent ();

         for (let y = 0; y < height; ++ y)
         {
            generator .ListIndent ();

            const s = y * width;

            for (let x = 0; x < width; ++ x)
            {
               generator .string += array [x + s];

               if (x + s !== length - 1)
                  generator .string += ',';

               if (x !== width - 1)
                  generator .TidySpace ();
            }

            if (y !== height - 1)
               generator .ListBreak ();
         }

         generator .DecIndent ();
      }
   },
});

for (const key of Object .keys (SFImage .prototype))
   Object .defineProperty (SFImage .prototype, key, { enumerable: false });

const width = {
   get ()
   {
      return this .getValue () .getWidth ();
   },
   set (value)
   {
      this .getValue () .setWidth (value);
      this .addEvent ();
   },
};

const height = {
   get ()
   {
      return this .getValue () .getHeight ();
   },
   set (value)
   {
      this .getValue () .setHeight (value);
      this .addEvent ();
   },
};

const comp = {
   get ()
   {
      return this .getValue () .getComp ();
   },
   set (value)
   {
      this .getValue () .setComp (value);
      this .addEvent ();
   },
};

const array = {
   get ()
   {
      return this .getValue () .getArray ();
   },
   set (value)
   {
      this .getValue () .setArray (value);
   },
};

Object .defineProperties (SFImage .prototype,
{
   x: width,
   y: height,
   width:  Object .assign ({ enumerable: true }, width),
   height: Object .assign ({ enumerable: true }, height),
   comp:   Object .assign ({ enumerable: true }, comp),
   array:  Object .assign ({ enumerable: true }, array),
});

Base_X3DField .addStaticProperties (SFImage, "SFImage");

const SFImage_default_ = SFImage;
;

/* harmony default export */ const Fields_SFImage = (Namespace/* default */.A .add ("SFImage", SFImage_default_));
;// ./src/x_ite/Fields.js














const Fields = {
   SFColor: Fields_SFColor,
   SFColorRGBA: Fields_SFColorRGBA,
   SFImage: Fields_SFImage,
   SFNode: Fields_SFNode,
   SFQuaternion: Fields_SFQuaternion,
   SFRotation: Fields_SFRotation,
   ... Fields_SFScalar,
   ... Fields_SFMatrix3,
   ... Fields_SFMatrix4,
   ... Fields_SFVec2,
   ... Fields_SFVec3,
   ... Fields_SFVec4,
   ... Fields_ArrayFields,
};

const Fields_default_ = Fields;
;

/* harmony default export */ const x_ite_Fields = (Namespace/* default */.A .add ("Fields", Fields_default_));
;// ./src/x_ite/Parser/HTMLSupport.js


const
   nodeTypeNames = new Map (), // (TYPENAME -> TypeName)
   fieldNames    = new Map (); // (fieldname -> fieldName)

const HTMLSupport =
{
   addConcreteNode ({ typeName, fieldDefinitions })
   {
      if (nodeTypeNames .has (typeName))
         return;

      this .addNodeTypeName (typeName);

      for (const { name, accessType } of fieldDefinitions)
      {
         if (accessType & Base_X3DConstants .initializeOnly)
            this .addFieldName (name)
      }
   },
   addNodeTypeName (typeName)
   {
      nodeTypeNames .set (typeName,                 typeName);
      nodeTypeNames .set (typeName .toUpperCase (), typeName);
   },
   getNodeTypeName (typeName)
   {
      return nodeTypeNames .get (typeName);
   },
   addFieldName (name)
   {
      fieldNames .set (name,                 name);
      fieldNames .set (name .toLowerCase (), name);
   },
   getFieldName (name)
   {
      return fieldNames .get (name);
   },
};

HTMLSupport .addNodeTypeName ("ProtoInstance");

const HTMLSupport_default_ = HTMLSupport;
;

/* harmony default export */ const Parser_HTMLSupport = (Namespace/* default */.A .add ("HTMLSupport", HTMLSupport_default_));
;// ./src/x_ite/Base/X3DBaseNode.js









const
   _browser           = Symbol (),
   _executionContext  = Symbol (),
   _type              = Symbol (),
   _fieldDefinitions  = Symbol .for ("X_ITE.X3DBaseNode.fieldDefinitions"),
   _predefinedFields  = Symbol (),
   _userDefinedFields = Symbol (),
   _childObjects      = Symbol (),
   _changedTypes      = Symbol (),
   _initialized       = Symbol (),
   _live              = Symbol (),
   _set_live__        = Symbol .for ("X_ITE.X3DBaseNode.set_live__");

function X3DBaseNode (executionContext, browser = executionContext .getBrowser ())
{
   if (this [_executionContext])
      return;

   Base_X3DChildObject .call (this);

   this [_browser]           = browser;
   this [_executionContext]  = executionContext;
   this [_type]              = [ ];
   this [_fieldDefinitions]  = this .constructor .fieldDefinitions ?? this [_fieldDefinitions];
   this [_predefinedFields]  = new Base_FieldArray ();
   this [_userDefinedFields] = new Base_FieldArray ();
   this [_childObjects]      = [ ];
   this [_live]              = true;
   this [_initialized]       = false;

   // Create fields.

   if (this .canUserDefinedFields ())
      this [_fieldDefinitions] = new Base_FieldDefinitionArray (this [_fieldDefinitions]);

   for (const fieldDefinition of this [_fieldDefinitions])
      this .addPredefinedField (fieldDefinition);

   // Create events.

   this .addChildObjects (Base_X3DConstants .outputOnly, "name_changed",     new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "typeName_changed", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "parents_changed",  new x_ite_Fields .SFTime ());
}

Object .assign (Object .setPrototypeOf (X3DBaseNode .prototype, Base_X3DChildObject .prototype),
{
   [_fieldDefinitions]: new Base_FieldDefinitionArray ([ ]),
   setName (value)
   {
      Base_X3DChildObject .prototype .setName .call (this, value)

      this ._name_changed = Date .now () / 1000;
   },
   getBrowser ()
   {
      return this [_browser];
   },
   getScene ()
   {
      return this [_executionContext] ?.getLocalScene () ?? null;
   },
   getExecutionContext ()
   {
      return this [_executionContext];
   },
   setExecutionContext (executionContext)
   {
      if (!this [_type] .includes (Base_X3DConstants .X3DScene))
         throw new Error (`${this .getTypeName ()}.setExecutionContext is not supported.`);

      // Disconnect interests.

      this .getOuterNode ?.()  ?.getLive () .removeInterest (_set_live__, this);
      this [_executionContext] ?.getLive () .removeInterest (_set_live__, this);
      this [_browser]           .getLive () .removeInterest (_set_live__, this);

      // Currently only useful for Scene.
      this [_executionContext] = executionContext;

      // Connect interests.

      if (this .getOuterNode ?.())
         this .getOuterNode () .getLive () .addInterest (_set_live__, this);

      else if (this [_executionContext])
         this [_executionContext] .getLive () .addInterest (_set_live__, this);

      else
         this [_browser] .getLive () .addInterest (_set_live__, this);

      this [_set_live__] ();
   },
   addType (value)
   {
      this [_type] .push (value);
   },
   getType ()
   {
      return this [_type];
   },
   setup ()
   {
      Object .freeze (this [_type]);

      this [_fieldDefinitions]  .addParent (this);
      this [_predefinedFields]  .addParent (this);
      this [_userDefinedFields] .addParent (this);

      for (const field of this [_childObjects])
         field .setTainted (false);

      for (const field of this [_predefinedFields])
         field .setTainted (false);

      for (const field of this [_userDefinedFields])
         field .setTainted (false);

      this .initialize ();

      this [_initialized] = true;
   },
   initialize ()
   { },
   isInitialized ()
   {
      return this [_initialized];
   },
   getInnerNode ()
   {
      return this;
   },
   isLive ()
   {
      ///  Returns the own live state of this node.

      return this [_live];
   },
   setLive (value)
   {
      ///  Sets the own live state of this node.  Setting the live state to false
      ///  temporarily disables this node completely.

      this [_live] = !!value .valueOf ();

      this [_set_live__] ();
   },
   getLive: (() =>
   {
      function getLive ()
      {
         return this ._live;
      }

      return function ()
      {
         ///  Returns the live event of this node.

         // Change function.

         Object .defineProperty (this, "getLive", { value: getLive });

         // Add isLive event.

         this .addChildObjects (Base_X3DConstants .outputOnly, "live", new x_ite_Fields .SFBool (this .checkLiveState ()));

         // Event processing is done manually and immediately, so:
         this ._live .removeParent (this);

         // Connect to execution context.

         if (this .getOuterNode ?.())
            this .getOuterNode () .getLive () .addInterest (_set_live__, this);

         else if (this [_executionContext] && this !== this [_browser])
            this [_executionContext] .getLive () .addInterest (_set_live__, this);

         else if (!this [_executionContext] && this !== this [_browser])
            this [_browser] .getLive () .addInterest (_set_live__, this);

         // Return field.

         return this ._live;
      };
   })(),
   checkLiveState ()
   {
      ///  Determines the live state of this node.

      if (this .getOuterNode ?.())
         return this [_live] && this .getOuterNode () .getLive () .getValue ();

      else if (this [_executionContext] && this !== this [_browser])
         return this [_live] && this [_executionContext] .getLive () .getValue ();

      else if (!this [_executionContext] && this !== this [_browser])
         return this [_live] && this [_browser] .isLive ();

      return this [_live];
   },
   [_set_live__] ()
   {
      const live = this ._live;

      if (!live)
         return;

      if (this .checkLiveState ())
      {
         if (live .getValue ())
            return;

         live .set (true);
         live .processEvent ();
      }
      else
      {
         if (live .getValue ())
         {
            live .set (false);
            live .processEvent ();
         }
      }
   },
   addChildObjects (... args /* accessType, name, field, ... */)
   {
      const length = args .length;

      for (let i = 0; i < length; i += 3)
         this .addChildObject (args [i], args [i + 1], args [i + 2]);
   },
   addChildObject (accessType, name, field)
   {
      this [_childObjects] .push (field);

      field .setPrivate (true);
      field .setTainted (true);
      field .addParent (this);
      field .setName (name);
      field .setAccessType (accessType);

      Object .defineProperty (this, `_${name}`,
      {
         get () { return field; },
         set (value) { field .setValue (value); },
      });
   },
   getFieldDefinition (name)
   {
      return this [_fieldDefinitions] .get (name);
   },
   getFieldDefinitions ()
   {
      return this [_fieldDefinitions];
   },
   getField (name, _throw = true)
   {
      const field = getFieldFromArray (this [_userDefinedFields], name)
         ?? getFieldFromArray (this [_predefinedFields], name);

      if (field)
         return field;

      if (_throw)
         throw new Error (`Unknown field '${name}' in node class ${this .getTypeName ()}.`);
   },
   getFields ()
   {
      return [... this [_predefinedFields], ... this [_userDefinedFields]];
   },
   addPredefinedField (fieldDefinition)
   {
      const
         accessType = fieldDefinition .getAccessType (),
         name       = fieldDefinition .getName (),
         field      = fieldDefinition .getValue () .copy ();

      field .setTainted (!this [_initialized]);
      field .addParent (this);
      field .setName (name);
      field .setAccessType (accessType);

      this [_predefinedFields] .add (name, field);

      Object .defineProperty (this, `_${name}`,
      {
         get () { return field; },
         set (value) { field .setValue (value); },
         configurable: true,
      });
   },
   addAlias (alias, field)
   {
      this [_predefinedFields] .alias (alias, field);

      if (field .isInitializable ())
         Parser_HTMLSupport .addFieldName (alias);
   },
   changeField (accessType, name, value)
   {
      const
         original = this [_predefinedFields] .get (name),
         field    = value .copy ();

      field .setTainted (!this [_initialized]);
      field .addParent (this);
      field .setName (name);
      field .setAccessType (accessType);

      this [_fieldDefinitions] = this [_fieldDefinitions] .copy ();
      this [_fieldDefinitions] .update (name, name, new Base_X3DFieldDefinition (accessType, name, value));
      this [_predefinedFields] .update (name, name, field);

      Object .defineProperty (this, `_${name}`,
      {
         get () { return field; },
         set (value) { field .setValue (value); },
         configurable: true,
      });

      this [_changedTypes] ??= new Map ();

      this [_changedTypes] .set (field, original);
   },
   removePredefinedField (name)
   {
      const field = this [_predefinedFields] .get (name);

      if (!field)
         return;

      field .removeParent (this);

      this [_predefinedFields] .remove (name);

      delete this [`_${field .getName ()}`];
   },
   getPredefinedField (name)
   {
      const field = getFieldFromArray (this [_predefinedFields], name);

      if (field)
         return field;

      throw new Error (`Unknown predefined field '${name}' in node class ${this .getTypeName ()}.`);
   },
   getPredefinedFields ()
   {
      return this [_predefinedFields];
   },
   canUserDefinedFields ()
   {
      return false;
   },
   addUserDefinedField (accessType, name, field)
   {
      if (!this .canUserDefinedFields ())
         throw new Error ("Couldn't add user-defined field, node does not support this.");

      if (this [_userDefinedFields] .has (name))
         this .removeUserDefinedField (name);

      field .setTainted (!this [_initialized]);
      field .addParent (this);
      field .setName (name);
      field .setAccessType (accessType);

      this [_fieldDefinitions] .remove (name);

      this [_fieldDefinitions]  .add (name, new Base_X3DFieldDefinition (accessType, name, field));
      this [_userDefinedFields] .add (name, field);
   },
   removeUserDefinedField (name)
   {
      const field = this [_userDefinedFields] .get (name);

      if (!field)
         return;

      field .removeParent (this);

      this [_fieldDefinitions]  .remove (name);
      this [_userDefinedFields] .remove (name);
   },
   getUserDefinedField (name)
   {
      const field = getFieldFromArray (this [_userDefinedFields], name);

      if (field)
         return field;

      throw new Error (`Unknown user-defined field '${name}' in node class ${this .getTypeName ()}.`);
   },
   getUserDefinedFields ()
   {
      return this [_userDefinedFields];
   },
   /**
    *
    * @param {boolean} extended  also return user-defined fields and fields with routes
    * @returns Array<X3DField>
    */
   getChangedFields (extended = false)
   {
      const changedFields = [ ];

      if (extended)
      {
         for (const field of this [_userDefinedFields])
            changedFields .push (field);
      }

      for (const field of this [_predefinedFields])
      {
         if (extended)
         {
            if (field .getInputRoutes () .size || field .getOutputRoutes () .size)
            {
               changedFields .push (field);
               continue;
            }
         }

         if (field .getReferences () .size === 0)
         {
            if (!field .isInitializable ())
               continue;

            if (this .isDefaultValue (field))
               continue;
         }

         changedFields .push (field);
      }

      return changedFields;
   },
   getConvertedFields (fields)
   {
      return this .convertFields (fields .map (field => this [_changedTypes] ?.get (field) ?? field));
   },
   convertFields (fields)
   {
      return fields;
   },
   isDefaultValue (field)
   {
      const
         name = field .getName (),
         f    = this [_userDefinedFields] .get (name) ?? this [_predefinedFields] .get (name);

      const fieldDefinition = f === field
         ? this [_fieldDefinitions] .get (name)
         : this .constructor .fieldDefinitions ?.get (name);

      return fieldDefinition ?.getValue () .equals (field) ?? field .getModificationTime () < 0;
   },
   getExtendedEventHandling ()
   {
      // Whether initializeOnly field are treated like inputOnly and inputOutput fields.
      return true;
   },
   addEvent (field)
   {
      if (field .isTainted ())
      {
         field .setModificationTime (Date .now () / 1000);
         return;
      }

      // if (this .getTypeName () === "HAnimMotion")
      // {
      //    console .log (this .getTypeName (), field .getName ());
      //    console .trace ();
      // }

      field .setTainted (true);

      this .addEventObject (field, Base_Events .create (field));
   },
   addEventObject (field, event)
   {
      const browser = this [_browser];

      field .setModificationTime (Date .now () / 1000);

      // Register for processEvent

      browser .addTaintedField (field, event);
      browser .addBrowserEvent ();

      // Register for eventsProcessed

      if (this .isTainted ())
         return;

      if (field .isInput () || (this .getExtendedEventHandling () && field .isInitializable ()))
      {
         this .addNodeEvent ();
      }
   },
   addNodeEvent ()
   {
      this .setModificationTime (Date .now () / 1000);

      if (this .isTainted ())
         return;

      // if (this .getTypeName () === "HAnimMotion")
      // {
      //    console .trace ();
      // }

      const browser = this [_browser];

      this .setTainted (true);
      browser .addTaintedNode (this);
      browser .addBrowserEvent ();
   },
   parentsChanged ()
   {
      const time = Date .now () / 1000;

      if (this [_executionContext])
      {
         this [_executionContext] ._sceneGraph_changed = time;
         this [_executionContext] ._bbox_changed       = time;
      }

      this ._parents_changed = time;
   },
   dispose ()
   {
      this .getOuterNode ?.()  ?.getLive () .removeInterest (_set_live__, this);
      this [_executionContext] ?.getLive () .removeInterest (_set_live__, this);
      this [_browser]          ?.getLive () .removeInterest (_set_live__, this);

      this .setLive (false);

      for (const field of this [_childObjects])
         field .dispose ();

      for (const field of this [_predefinedFields])
         field .dispose ();

      for (const field of this [_userDefinedFields])
         field .dispose ();

      Base_X3DChildObject .prototype .dispose .call (this);
   },
});

const getFieldFromArray = (() =>
{
   const
      set_field     = /^set_(.*?)$/,
      field_changed = /^(.*?)_changed$/;

   return function (array, name)
   {
      const field = array .get (name);

      if (field)
         return field;

      const match = name .match (set_field);

      if (match)
      {
         const field = array .get (match [1]);

         if (field ?.getAccessType () === Base_X3DConstants .inputOutput)
            return field;
      }
      else
      {
         const match = name .match (field_changed);

         if (match)
         {
            const field = array .get (match [1]);

            if (field ?.getAccessType () === Base_X3DConstants .inputOutput)
               return field;
         }
      }
   };
})();

for (const key of Object .keys (X3DBaseNode .prototype))
   Object .defineProperty (X3DBaseNode .prototype, key, { enumerable: false });

Object .defineProperties (X3DBaseNode .prototype,
{
   name_changed:
   {
      get () { return this ._name_changed; },
   },
   typeName_changed:
   {
      get () { return this ._typeName_changed; },
   },
   parents_changed:
   {
      get () { return this ._parents_changed; },
   },
});

const X3DBaseNode_default_ = X3DBaseNode;
;

/* harmony default export */ const Base_X3DBaseNode = (Namespace/* default */.A .add ("X3DBaseNode", X3DBaseNode_default_));
;// ./src/x_ite/Browser/Legacy.js
/* provided dependency */ var Legacy_$ = __webpack_require__(44).A;
const Legacy =
{
   elements (elements, X3DBrowser)
   {
      if (!elements .length)
         return;

      console .warn ("Use of <X3DCanvas> element is deprecated, please use <x3d-canvas> element instead. See https://create3000.github.io/x_ite/#embedding-x_ite-within-a-web-page.");

      Legacy_$.map (elements, element => new X3DBrowser (element));
   },
   properties (browser, properties)
   {
      const element = browser .getElement ();

      if (element .prop ("nodeName") .toUpperCase () !== "X3DCANVAS")
         return properties;

      for (const [name, property] of Object .entries (properties))
      {
         const set = property .set;

         property .set = function (value)
         {
            set (value);

            browser .attributeChangedCallback (name, undefined, value);
         };
      }

      return properties;
   },
   browser (browser)
   {
      const element = browser .getElement ();

      if (element .prop ("nodeName") .toUpperCase () !== "X3DCANVAS")
         return;

      // Make element focusable.
      element .attr ("tabindex", element .attr ("tabindex") ?? 0);

      // Process initial attributes.
      for (const { name, value } of element [0] .attributes)
         browser .attributeChangedCallback (name, undefined, value);
   },
   error (elements, error)
   {
      console .error (error);

      // <X3DCanvas>
      elements .children (".x_ite-private-browser") .hide ();
      elements .children (":not(.x_ite-private-browser)") .show ();
   },
};

const Legacy_default_ = Legacy;
;

/* harmony default export */ const Browser_Legacy = (Namespace/* default */.A .add ("Legacy", Legacy_default_));
;// ./src/x_ite/BROWSER_VERSION.js
const BROWSER_VERSION_default_ = "15.1.8";
;

/* harmony default export */ const BROWSER_VERSION = (Namespace/* default */.A .add ("BROWSER_VERSION", BROWSER_VERSION_default_));
;// ./src/x_ite/Browser/Core/Context.js
const Context =
{
   excludes: new Set ([
      "WEBGL_debug_renderer_info",
      "WEBGL_polygon_mode",
   ]),
   create (canvas, preserveDrawingBuffer)
   {
      const gl = canvas .getContext ("webgl2", { antialias: false, preserveDrawingBuffer });

      if (!gl)
         throw new Error ("Couldn't create WebGL context.");

      gl .getVersion = () => 2;

      // Load extensions.

      for (const extension of gl .getSupportedExtensions () .filter (extension => !this .excludes .has (extension)))
         gl .getExtension (extension);

      // // Feature detection:

      // // console .log (check32BitTextures (gl));

      // if (!check32BitTextures (gl))
      // {
      //    // Use defineProperty to overwrite property.
      //    Object .defineProperty (gl, "RGBA32F",
      //    {
      //       value: gl .RGBA16F,
      //       configurable: true,
      //       enumerable: true,
      //    });
      // }

      return gl;
   },
}

// function check32BitTextures (gl)
// {
//    // Create framebuffer.
//    const colorTexture = gl .createTexture ();
//    const framebuffer  = gl .createFramebuffer ();

//    gl .bindTexture (gl .TEXTURE_2D, colorTexture);

//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);

//    gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA, 1, 1, 0, gl .RGBA, gl .UNSIGNED_BYTE, new Uint8Array (4));

//    gl .bindFramebuffer (gl.FRAMEBUFFER, framebuffer);
//    gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .TEXTURE_2D, colorTexture, 0);

//    if (gl .checkFramebufferStatus (gl .FRAMEBUFFER) !== gl .FRAMEBUFFER_COMPLETE)
//       return false;

//    // Vertex shader (pass through)
//    const vsSource = `#version 300 es
// precision highp float;
// in vec2 x3d_Vertex;
// void
// main ()
// {
//    gl_Position = vec4 (x3d_Vertex, 0.0, 1.0);
// }
//   `;

//   // Fragment shader
//   const fsSource = `#version 300 es
// precision highp float;
// precision highp sampler2D;
// uniform sampler2D x3d_Texture;
// out vec4 x3d_FragColor;
// void
// main ()
// {
//    vec4 pixel = texture (x3d_Texture, vec2 (0.5, 0.5));

//    float r = pixel .r == 0.0        ? 1.0 : 0.0;
//    float g = pixel .g == 1.0        ? 1.0 : 0.0;
//    float b = pixel .b == 16777216.0 ? 1.0 : 0.0;
//    float a = pixel .a == 16777216.0 ? 1.0 : 0.0;

//    x3d_FragColor = vec4 (r, g, b, a);
// }
//   `;

//    // Create program.
//    const program = createProgram (gl, vsSource, fsSource);
//    gl .useProgram (program);

//    // Quad covering the whole clip space.
//    const quadVertices = new Float32Array ([
//       -1, -1,
//        1, -1,
//       -1,  1,
//       -1,  1,
//        1, -1,
//        1,  1,
//    ]);

//    // Create buffer for vertices.
//    const buffer = gl .createBuffer ();
//    gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
//    gl .bufferData (gl .ARRAY_BUFFER, quadVertices, gl .STATIC_DRAW);

//    // Bind vertex attrib.
//    const vertexLocation = gl .getAttribLocation (program, "x3d_Vertex");
//    gl .enableVertexAttribArray (vertexLocation);
//    gl .vertexAttribPointer (vertexLocation, 2, gl .FLOAT, false, 0, 0);

//    // Create texture.
//    const pixelData = new Float32Array ([0, 1, 2**24, 2**24+1]);
//    const texture   = gl .createTexture ();
//    const sampler   = gl .getUniformLocation (program, "x3d_Texture");

//    gl .activeTexture (gl .TEXTURE0);
//    gl .bindTexture (gl .TEXTURE_2D, texture);

//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
//    gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);

//    gl .uniform1i (sampler, 0);
//    gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA32F, 1, 1, 0, gl .RGBA, gl .FLOAT, pixelData);

//    gl .clearColor (0, 0, 0, 0);
//    gl .clear (gl .COLOR_BUFFER_BIT);
//    gl .drawArrays (gl .TRIANGLES, 0, 6);

//    const readData = new Uint8Array (4);
//    gl .readPixels (0, 0, 1, 1, gl .RGBA, gl .UNSIGNED_BYTE, readData);

//    // console .log ("Original data:",  ... pixelData);
//    // console .log ("Read-back data:", ... readData);
//    // $(".x_ite-console") .append (document .createTextNode (readData + "\n"))

//    // Cleanup
//    gl .deleteFramebuffer (framebuffer);
//    gl .deleteTexture (colorTexture);
//    gl .deleteTexture (texture);
//    gl .deleteBuffer (buffer);
//    gl .deleteProgram (program);

//    return readData .every (p => p === 255);
// }

// // Shader compile helper
// function createShader (gl, type, source)
// {
//    const shader = gl.createShader (type);
//    gl .shaderSource (shader, source);
//    gl .compileShader(shader);
//    return shader;
// }

// // Program link helper
// function createProgram (gl, vsSource, fsSource)
// {
//    const vs = createShader (gl, gl.VERTEX_SHADER, vsSource);
//    const fs = createShader (gl, gl.FRAGMENT_SHADER, fsSource);
//    const program = gl .createProgram ();
//    gl .attachShader (program, vs);
//    gl .attachShader (program, fs);
//    gl .linkProgram (program);
//    return program;
// }

const Context_default_ = Context;
;

/* harmony default export */ const Core_Context = (Namespace/* default */.A .add ("Context", Context_default_));
;// ./src/standard/Time/StopWatch.js
class StopWatch
{
   #startTime = 0;
   #stopTime = 0;
   cycles = 0;
   elapsedTime = 0;

   start ()
   {
      this .#startTime = Date .now ();
   }

   stop ()
   {
      if (this .#startTime <= this .#stopTime)
         return;

      this .#stopTime    = Date .now ();
      this .cycles      += 1;
      this .elapsedTime += this .#stopTime - this .#startTime;
   }

   reset ()
   {
      this .#startTime  = 0;
      this .#stopTime   = 0;
      this .cycles      = 0;
      this .elapsedTime = 0;
   }

   get averageTime ()
   {
      return this .cycles ? this .elapsedTime / this .cycles : 0;
   }
}

const StopWatch_default_ = StopWatch;
;

/* harmony default export */ const Time_StopWatch = (Namespace/* default */.A .add ("StopWatch", StopWatch_default_));
;// ./src/x_ite/Browser/Shape/GeometryType.js
let i = 0;

const GeometryType = {
   POINT:    i ++,
   LINE:     i ++,
   TRIANGLE: i ++,
   QUAD:     i ++,
   SPRITE:   i ++,
   GEOMETRY: i ++,
};

const GeometryType_default_ = Object .assign (new Map (Object .entries (GeometryType)), GeometryType);
;

/* harmony default export */ const Shape_GeometryType = (Namespace/* default */.A .add ("GeometryType", GeometryType_default_));
;// ./src/locale/dictionary.js
const dictionary_default_ = [
   {
      en: "Less Properties",
      de: "Weniger Eigenschaften",
      fr: "Moins de Propriétés",
   },
   {
      en: "More Properties",
      de: "Mehr Eigenschaften",
      fr: "Plus de Propriétés",
   },
   {
      en: "Frame rate",
      de: "Bildrate",
      fr: "Fréquence d'Images",
   },
   {
      en: "fps",
      de: "BpS",
      fr: "",
   },
   {
      en: "Speed",
      de: "Geschwindigkeit",
      fr: "Vélocité",
   },
   {
      en: "m/s",
      de: "",
      fr: "",
   },
   {
      en: "km/h",
      de: "",
      fr: "",
   },
   {
      en: "Browser",
      de: "",
      fr: "Navigateur",
   },
   {
      en: "ms",
      de: "",
      fr: "",
   },
   {
      en: "X3D total",
      de: "X3D gesamt",
      fr: "X3D total",
   },
   {
      en: "Event Processing",
      de: "Ereignisverarbeitung",
      fr: "Traitement des Événements",
   },
   {
      en: "Pointer",
      de: "Zeigegerät",
      fr: "Pointeur",
   },
   {
      en: "Camera",
      de: "Kamera",
      fr: "Caméra",
   },
   {
      en: "Collision Detection",
      de: "Kollisionserkennung",
      fr: "Détection des Collisions",
   },
   {
      en: "Rendering",
      de: "Rendering",
      fr: "Rendement",
   },
   {
      en: "Number of Primitives",
      de: "Anzahl der Grundobjekte",
      fr: "Nombre d'Objets de Base",
   },
   {
      en: "Number of Shapes",
      de: "Anzahl der Formen",
      fr: "Nombre de Formes",
   },
   {
      en: "Number of Sensors",
      de: "Anzahl der Sensoren",
      fr: "Nombre de Senseurs",
   },
   {
      en: "Browser Timings",
      de: "Zeitberechnung",
      fr: "Calcul du Temps",
   },
   {
      en: "Viewpoints",
      de: "Ansichtspunkte",
      fr: "Points de Vue",
   },
   {
      en: "Available Viewers",
      de: "Verfügbare Betrachter",
      fr: "Visionneurs disponibles",
   },
   {
      en: "Straighten Horizon",
      de: "Horizont gerade richten",
      fr: "Redresser l'Horizon",
   },
   {
      en: "Primitive Quality",
      de: "Qualität der Grundobjekte",
      fr: "Qualité des Objets simples",
   },
   {
      en: "High",
      de: "Hoch",
      fr: "Haut",
   },
   {
      en: "high",
      de: "hoch",
      fr: "haut",
   },
   {
      en: "Medium",
      de: "Mittel",
      fr: "Moyenne",
   },
   {
      en: "medium",
      de: "mittel",
      fr: "moyenne",
   },
   {
      en: "Low",
      de: "Niedrig",
      fr: "Faible",
   },
   {
      en: "low",
      de: "niedrig",
      fr: "faible",
   },
   {
      en: "Texture Quality",
      de: "Textur Qualität",
      fr: "Qualité des Textures",
   },
   {
      en: "Display Rubberband",
      de: "Gummiband anzeigen",
      fr: "Présenter le Bande élastique",
   },
   {
      en: "Rubberband",
      de: "Gummiband",
      fr: "Bande élastique",
   },
   {
      en: "on",
      de: "an",
      fr: "marche",
   },
   {
      en: "off",
      de: "aus",
      fr: "arrêt",
   },
   {
      en: "Mute Browser",
      de: "Browser stumm schalten",
      fr: "Rendre Navigateur muet",
   },
   {
      en: "Browser muted",
      de: "Browser stumm geschalten",
      fr: "Navigateur muet",
   },
   {
      en: "Browser unmuted",
      de: "Browser Ton an",
      fr: "Son de Navigateur réactivé",
   },
   {
      en: "Leave Fullscreen",
      de: "Vollbild verlassen",
      fr: "Laisser le plein Écran",
   },
   {
      en: "Fullscreen",
      de: "Vollbild",
      fr: "Plein Écran",
   },
   {
      en: "About X_ITE",
      de: "Über X_ITE",
      fr: "À propos de X_ITE",
   },
   {
      en: "Examine Viewer",
      de: "Untersuchen",
      fr: "Examiner",
   },
   {
      en: "Walk Viewer",
      de: "Laufen",
      fr: "Aller",
   },
   {
      en: "Fly Viewer",
      de: "Fliegen",
      fr: "Voler",
   },
   {
      en: "Plane Viewer",
      de: "Ebenen Betrachter",
      fr: "Visionneur de la Plaine",
   },
   {
      en: "Look At Viewer",
      de: "Auf Objekte zielen",
      fr: "Regarder un Objet de près",
   },
   {
      en: "None Viewer",
      de: "Kein Betrachter",
      fr: "Pas de Visionneur",
   },
   {
      en: "Points",
      de: "Punkte",
      fr: "",
   },
   {
      en: "Flat",
      de: "Flach",
      fr: "Plat",
   },
   {
      en: "Wireframe",
      de: "Drahtgittermodel",
      fr: "Image filaire",
   },
   {
      en: "Loading %1 file",
      de: "Lade %1 Datei",
      fr: "Télécharger %1 Fichier",
   },
   {
      en: "Loading %1 files",
      de: "Lade %1 Dateien",
      fr: "Télécharger %1 Fichiers",
   },
   {
      en: "Loading done",
      de: "Fertig mit Laden",
      fr: "Téléchargement fini",
   },
   {
      en: "Failed loading world.",
      de: "Laden der Dateien fehlgeschlagen.",
      fr: "Le chargement des Fichiers a échoué.",
   },
   {
      en: "Show World Info",
      de: "World Info anzeigen",
      fr: "Afficher World Info",
   },
   {
      en: "Viewpoint is copied to clipboard.",
      de: "Ansichtspunkt wurde in die Zwischenablage kopiert.",
      fr: "Le point de Vue est copié dans le Presse-Papiers.",
   },
   {
      en: "Animations",
      de: "Animationen",
      fr: "",
   },
   {
      en: "All",
      de: "Alle",
      fr: "Toutes",
   },
   {
      en: "None",
      de: "Keine",
      fr: "Aucune",
   },
   {
      en: "Start WebXR session.",
      de: "Starte eine WebXR Session.",
      fr: "Démarrez une session WebXR.",
   },
   {
      en: "Stop WebXR session.",
      de: "Beende die WebXR Session.",
      fr: "Terminez la session WebXR.",
   },
   {
      en: "Activate sound.",
      de: "Activiere den Ton.",
      fr: "Activez le son.",
   },
   {
      en: "Points; Lines; Triangles",
      de: "Punkte; Linien; Dreiecke",
      fr: "Points; Lignes; Triangles",
   },
   {
      en: "Opaque Shapes + Transparent Shapes",
      de: "opaque Formen + transparente Formen",
      fr: "Formes opaques + Formes transparentes",
   },
];
;

/* harmony default export */ const dictionary = (Namespace/* default */.A .add ("dictionary", dictionary_default_));
;// ./src/locale/gettext.js


const locales = new Set (Object .keys (dictionary [0]));

function getLanguage ()
{
   for (const value of navigator .languages)
   {
      const language = value .split ("-") [0];

      if (locales .has (language))
         return language;
   }

   return (navigator .language || navigator .userLanguage) .split ("-") [0];
}

const
   language = getLanguage (),
   locale   = new Map (dictionary .map (p => [p .en, p [language]]));

const gettext_default_ = (string) => locale .get (string) || string;
;

/* harmony default export */ const gettext = (Namespace/* default */.A .add ("gettext", gettext_default_));
;// ./src/x_ite/Browser/Core/BrowserTimings.js
/* provided dependency */ var BrowserTimings_$ = __webpack_require__(44).A;






function BrowserTimings (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .localStorage  = this .getBrowser () .getLocalStorage () .addNameSpace ("BrowserTimings.");
   this .fps           = new Time_StopWatch ();
   this .primitives    = { };
}

Object .assign (Object .setPrototypeOf (BrowserTimings .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .getBrowser () .getBrowserOptions () ._Timings .addInterest ("set_enabled__", this);

      this .localStorage .setDefaultValues ({ type: "LESS" });

      this .element = BrowserTimings_$("<div></div>")
         .addClass (["x_ite-private-browser-timings", "x_ite-private-hidden"])
         .appendTo (this .getBrowser () .getSurface ());

      this .table = BrowserTimings_$("<table></table>")
         .appendTo (this .element);

      this .header = BrowserTimings_$("<thead></thead>")
         .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<th colspan='2'></th>")
         .text (gettext("Browser Timings"))))
         .appendTo (this .table);

      this .body = BrowserTimings_$("<tbody></tbody>")
         .appendTo (this .table);

      this .footer = BrowserTimings_$("<tfoot></tfoot>")
         .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td colspan='2'></td>")))
         .appendTo (this .table);

      this .button = BrowserTimings_$("<button></button>")
         .appendTo (this .footer .find ("td"));

      this .frameRate       = BrowserTimings_$("<td></td>");
      this .speed           = BrowserTimings_$("<td></td>");
      this .frameRate       = BrowserTimings_$("<td></td>");
      this .speed           = BrowserTimings_$("<td></td>");
      this .browserTime     = BrowserTimings_$("<td></td>");
      this .x3dTotal        = BrowserTimings_$("<td></td>");
      this .eventProcessing = BrowserTimings_$("<td></td>");
      this .pointerTime     = BrowserTimings_$("<td></td>");
      this .cameraTime      = BrowserTimings_$("<td></td>");
      this .pickingTime     = BrowserTimings_$("<td></td>");
      this .collisionTime   = BrowserTimings_$("<td></td>");
      this .renderTime      = BrowserTimings_$("<td></td>");
      this .numPrimitives   = BrowserTimings_$("<td></td>") .addClass ("pointer") .attr ("title", gettext("Points; Lines; Triangles"));
      this .numShapes       = BrowserTimings_$("<td></td>") .addClass ("pointer") .attr ("title", gettext("Opaque Shapes + Transparent Shapes"));
      this .sensors         = BrowserTimings_$("<td></td>");

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Frame rate") + ":"))
         .append (this .frameRate));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Speed") + ":"))
         .append (this .speed)
         .addClass ("x_ite-private-more"));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Browser") + ":"))
         .append (this .browserTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("X3D total") + ":"))
         .append (this .x3dTotal));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Event Processing") + ":"))
         .append (this .eventProcessing));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Pointer") + ":"))
         .append (this .pointerTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Camera") + ":"))
         .append (this .cameraTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Picking") + ":"))
         .append (this .pickingTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Collision Detection") + ":"))
         .append (this .collisionTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Rendering") + ":"))
         .append (this .renderTime));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Number of Primitives") + ":"))
         .append (this .numPrimitives));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Number of Shapes") + ":"))
         .append (this .numShapes));

      this .body .append (BrowserTimings_$("<tr></tr>")
         .append (BrowserTimings_$("<td></td>") .text (gettext("Number of Sensors") + ":"))
         .append (this .sensors));

      this .button .on ("click touchend", this .set_type__ .bind (this));

      this .localStorage .type = this .localStorage .type === "MORE" ? "LESS" : "MORE";

      this .set_type__ ();
   },
   set_enabled__ ()
   {
      if (this .getBrowser () .getBrowserOption ("Timings"))
      {
         this .element
            .removeClass (["x_ite-private-fade-out-300", "x_ite-private-hidden"])
            .addClass ("x_ite-private-fade-in-300");

         this .getBrowser () .addBrowserCallback (this, Base_X3DConstants .INITIALIZED_EVENT, () => this .reset ());
         this .getBrowser () .prepareEvents () .addInterest ("update", this);
         this .reset ();
      }
      else
      {
         this .element
            .removeClass ("x_ite-private-fade-in-300")
            .addClass ("x_ite-private-fade-out-300");

         this .getBrowser () .removeBrowserCallback (this, Base_X3DConstants .INITIALIZED_EVENT);
         this .getBrowser () .prepareEvents () .removeInterest ("update", this);
      }
   },
   set_type__ ()
   {
      if (this .localStorage .type === "MORE")
      {
         this .localStorage .type = "LESS";
         this .table .addClass ("less");
         this .table .removeClass ("more");
      }
      else
      {
         this .localStorage .type = "MORE";
         this .table .addClass ("more");
         this .table .removeClass ("less");
      }

      this .set_button__ ();
      this .build ();
   },
   set_button__ ()
   {
      if (this .localStorage .type === "MORE")
         this .button .text (gettext("Less Properties"));
      else
         this .button .text (gettext("More Properties"));
   },
   async reset ()
   {
      await this .getBrowser () .nextFrame ();

      this .fps .reset ();
      this .build ();
   },
   update ()
   {
      this .fps .stop ();

      if (this .fps .elapsedTime > 1000)
      {
         this .build ();
         this .fps .reset ();
      }

      this .fps .start ();
   },
   build ()
   {
      const browser = this .getBrowser ();

      if (this .fps .elapsedTime)
      {
         this .frameRate .text (`${f2 (1000 / this .fps .averageTime)} ${gettext("fps")}`);
         this .speed .text (`${f2 (this .getSpeed (browser .currentSpeed ))} ${this .getSpeedUnit (browser .currentSpeed)}`);
      }
      else
      {
         this .frameRate .text (`${f2 (0)} ${gettext("fps")}`);
         this .speed .text (`${f2 (this .getSpeed (0))} ${this .getSpeedUnit (0)}`);
      }

      if (this .localStorage .type !== "MORE" || !browser .getWorld ())
         return;

      const
         layers            = browser .getWorld () .getLayerSet () .getLayers (),
         activeLayer       = browser .getActiveLayer (),
         navigationTime    = activeLayer ?.getCollisionTime () .averageTime ?? 0,
         collisionTime     = browser .getCollisionTime () .averageTime + navigationTime,
         routingTime       = Math .max (0, browser .getBrowserTime () .averageTime - (browser .getCameraTime () .averageTime + browser .getCollisionTime () .averageTime + browser .getDisplayTime () .averageTime)),
         prepareEvents     = browser .prepareEvents () .getInterests () .size - 1,
         sensors           = browser .timeEvents () .getInterests () .size + browser .sensorEvents () .getInterests () .size + browser .getCollisionCount (),
         primitives        = this .getPrimitives (layers),
         opaqueShapes      = this .getOpaqueShapes (layers),
         transparentShapes = this .getTransparentShapes (layers);

      this .browserTime     .text (`${f2 (browser .getSystemTime () .averageTime)} ${gettext("ms")}`);
      this .x3dTotal        .text (`${f2 (browser .getBrowserTime () .averageTime)} ${gettext("ms")}`);
      this .eventProcessing .text (`${f2 (routingTime)} ${gettext("ms")}`);
      this .pointerTime     .text (`${f2 (browser .getPointingTime () .averageTime)} ${gettext("ms")}`);
      this .cameraTime      .text (`${f2 (browser .getCameraTime () .averageTime)} ${gettext("ms")}`);
      this .pickingTime     .text (`${f2 (browser .getPickingTime () .averageTime)} ${gettext("ms")}`);
      this .collisionTime   .text (`${f2 (collisionTime)} ${gettext("ms")}`);
      this .renderTime      .text (`${f2 (browser .getDisplayTime () .averageTime)} ${gettext("ms")}`);
      this .numPrimitives   .text (`${f0 (primitives .points)}; ${f0 (primitives .lines)}; ${f0 (primitives .triangles)}`);
      this .numShapes       .text (`${f0 (opaqueShapes)} + ${f0 (transparentShapes)}`);
      this .sensors         .text (f0 (prepareEvents + sensors));

      browser .getSystemTime ()    .reset ();
      browser .getBrowserTime ()   .reset ();
      browser .getPointingTime ()  .reset ();
      browser .getCameraTime ()    .reset ();
      browser .getPickingTime ()   .reset ();
      browser .getCollisionTime () .reset ();
      browser .getDisplayTime ()   .reset ();

      activeLayer ?.getCollisionTime () .reset ();
   },
   getSpeed (speed)
   {
      if (speed < 15)
         return speed;

      return speed * 3.6;
   },
   getSpeedUnit (speed)
   {
      if (speed < 15)
         return gettext("m/s");

      return gettext("km/h");
   },
   getPrimitives (layerNodes)
   {
      this .primitives .points    = 0;
      this .primitives .lines     = 0;
      this .primitives .triangles = 0;

      for (const layerNode of layerNodes)
      {
         const
            numOpaqueShapes      = layerNode .getNumOpaqueShapes (),
            numTransparentShapes = layerNode .getNumTransparentShapes (),
            opaqueShapes         = layerNode .getOpaqueShapes (),
            transparentShapes    = layerNode .getTransparentShapes ();

         this .countPrimitives (opaqueShapes,      numOpaqueShapes);
         this .countPrimitives (transparentShapes, numTransparentShapes);
      }

      return this .primitives;
   },
   countPrimitives (shapes, numShapes)
   {
      for (let i = 0; i < numShapes; ++ i)
      {
         const
            shapeNode    = shapes [i] .shapeNode,
            numInstances = shapeNode .getNumInstances ();

         if (!shapeNode .getExecutionContext () .getCountPrimitives ())
            continue;

         switch (shapeNode .getGeometryType ())
         {
            case Shape_GeometryType .POINT:
            {
               this .primitives .points += numInstances;
               continue;
            }
            case Shape_GeometryType .LINE:
            {
               this .primitives .lines += numInstances;
               continue;
            }
            case Shape_GeometryType .TRIANGLE:
            case Shape_GeometryType .QUAD:
            case Shape_GeometryType .SPRITE:
            {
               this .primitives .triangles += numInstances * 2;
               continue;
            }
            case Shape_GeometryType .GEOMETRY:
            {
               const geometryNode = shapeNode .getGeometry ();

               // ParticleSystem nodes may have no geometry.
               if (!geometryNode)
                  continue;

               const vertices = geometryNode .getVertices () .length / 4 * numInstances;

               switch (geometryNode .getGeometryType ())
               {
                  case 0:
                  {
                     this .primitives .points += vertices;
                     break;
                  }
                  case 1:
                  {
                     this .primitives .lines += vertices / 2;
                     break;
                  }
                  case 2:
                  case 3:
                  {
                     this .primitives .triangles += vertices / 3;
                     break;
                  }
               }

               continue;
            }
         }
      }
   },
   getOpaqueShapes (layers)
   {
      return layers .reduce ((n, layer) => n + this .countShapes (layer .getOpaqueShapes (), layer .getNumOpaqueShapes ()), 0);
   },
   getTransparentShapes (layers)
   {
      return layers .reduce ((n, layer) => n + this .countShapes (layer .getTransparentShapes (), layer .getNumTransparentShapes ()), 0);
   },
   countShapes (shapes, numShapes)
   {
      let count = 0;

      for (let i = 0; i < numShapes; ++ i)
      {
         if (!shapes [i] .shapeNode .getExecutionContext () .getCountPrimitives ())
            continue;

         ++ count;
      }

      return count;
   },
});

Object .defineProperties (BrowserTimings,
{
   typeName:
   {
      value: "BrowserTimings",
      enumerable: true,
   },
});

function format (minimumFractionDigits, maximumFractionDigits)
{
   const format = new Intl .NumberFormat (navigator .language || navigator .userLanguage, {
      notation: "standard",
      minimumFractionDigits,
      maximumFractionDigits,
   }) .format;

   return function (value)
   {
      return format (Number .isFinite (value) ? value : 0);
   };
}

const
   f0 = format (0, 0),
   f2 = format (2, 2);

const BrowserTimings_default_ = BrowserTimings;
;

/* harmony default export */ const Core_BrowserTimings = (Namespace/* default */.A .add ("BrowserTimings", BrowserTimings_default_));
;// ./src/x_ite/Browser/Core/PrimitiveQuality.js
let PrimitiveQuality_i = 0;

const PrimitiveQuality =
{
   LOW:    PrimitiveQuality_i ++,
   MEDIUM: PrimitiveQuality_i ++,
   HIGH:   PrimitiveQuality_i ++,
};

const PrimitiveQuality_default_ = Object .assign (new Map (Object .entries (PrimitiveQuality)), PrimitiveQuality);
;

/* harmony default export */ const Core_PrimitiveQuality = (Namespace/* default */.A .add ("PrimitiveQuality", PrimitiveQuality_default_));
;// ./src/x_ite/Browser/Core/Shading.js
let Shading_i = 0;

const Shading =
{
   POINT:     Shading_i ++,
   WIREFRAME: Shading_i ++,
   FLAT:      Shading_i ++,
   GOURAUD:   Shading_i ++,
   PHONG:     Shading_i ++,
};

const Shading_default_ = Object .assign (new Map (Object .entries (Shading)), Shading);
;

/* harmony default export */ const Core_Shading = (Namespace/* default */.A .add ("Shading", Shading_default_));
;// ./src/x_ite/Browser/Core/TextureQuality.js


const TextureQuality_default_ = Core_PrimitiveQuality;
;

/* harmony default export */ const TextureQuality = (Namespace/* default */.A .add ("TextureQuality", TextureQuality_default_));
;// ./src/x_ite/Browser/Core/TextCompression.js
let TextCompression_i = 0;

const TextCompression =
{
   CHAR_SPACING: TextCompression_i ++,
   SCALING:      TextCompression_i ++,
};

const TextCompression_default_ = TextCompression;
;

/* harmony default export */ const Core_TextCompression = (Namespace/* default */.A .add ("TextCompression", TextCompression_default_));
;// ./src/x_ite/Browser/Core/BrowserOptions.js
/* provided dependency */ var BrowserOptions_$ = __webpack_require__(44).A;











// https://www.web3d.org/documents/specifications/19775-1/V4.0/Part01/components/networking.html#BrowserProperties
// String values are considered to be case insensitive.

function BrowserOptions (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addAlias ("AntiAliased", this ._Antialiased);

   const browser = this .getBrowser ();

   this .localStorage     = browser .getLocalStorage () .addNameSpace ("BrowserOptions.");
   this .textureQuality   = TextureQuality .MEDIUM
   this .primitiveQuality = Core_PrimitiveQuality .MEDIUM;
   this .shading          = Core_Shading .GOURAUD;
}

Object .assign (Object .setPrototypeOf (BrowserOptions .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .localStorage .setDefaultValues ({
         Rubberband:        this ._Rubberband        .getValue (),
         PrimitiveQuality:  this ._PrimitiveQuality  .getValue (),
         TextureQuality:    this ._TextureQuality    .getValue (),
         StraightenHorizon: this ._StraightenHorizon .getValue (),
         Timings:           this ._Timings           .getValue (),
      });

      this ._Rubberband                   .addInterest ("set_Rubberband__",                   this);
      this ._Antialiased                  .addInterest ("set_Antialiased__",                  this);
      this ._PrimitiveQuality             .addInterest ("set_PrimitiveQuality__",             this);
      this ._TextureQuality               .addInterest ("set_TextureQuality__",               this);
      this ._Shading                      .addInterest ("set_Shading__",                      this);
      this ._StraightenHorizon            .addInterest ("set_StraightenHorizon__",            this);
      this ._AutoUpdate                   .addInterest ("set_AutoUpdate__",                   this);
      this ._ContentScale                 .addInterest ("set_ContentScale__",                 this);
      this ._DisplayColorSpace            .addInterest ("set_DisplayColorSpace",              this);
      this ._Exposure                     .addInterest ("set_Exposure__",                     this);
      this ._LogarithmicDepthBuffer       .addInterest ("set_LogarithmicDepthBuffer__",       this);
      this ._Multisampling                .addInterest ("set_Multisampling__",                this);
      this ._Mute                         .addInterest ("set_Mute__",                         this);
      this ._OrderIndependentTransparency .addInterest ("set_OrderIndependentTransparency__", this);
      this ._Timings                      .addInterest ("set_Timings__",                      this);
      this ._XRSessionMode                .addInterest ("set_XRSessionMode__",                this);

      this .set_Antialiased__                  (this ._Antialiased);
      this .set_Shading__                      (this ._Shading);
      this .set_AutoUpdate__                   (this ._AutoUpdate);
      this .set_ContentScale__                 (this ._ContentScale);
      this .set_DisplayColorSpace              (this ._DisplayColorSpace);
      this .set_Exposure__                     (this ._Exposure);
      this .set_LogarithmicDepthBuffer__       (this ._LogarithmicDepthBuffer);
      this .set_Multisampling__                (this ._Multisampling);
      this .set_OrderIndependentTransparency__ (this ._OrderIndependentTransparency);
      this .set_XRSessionMode__                (this ._XRSessionMode);

      this .reset ();
   },
   reset: (() =>
   {
      const attributes = new Set ([
         "Antialiased",
         "AutoUpdate",
         "Cache",
         "ColorSpace",
         "ContentScale",
         "ContextMenu",
         "Debug",
         "DisplayColorSpace",
         "Exposure",
         "LogarithmicDepthBuffer",
         "MaximumFrameRate",
         "Multisampling",
         "Notifications",
         "OrderIndependentTransparency",
         "SplashScreen",
         "TextCompression",
         "ToneMapping",
         "XRSessionMode",
      ]);

      const mappings = new Map ([
         ["AutoUpdate",    "update"],
         ["XRSessionMode", "xrSessionMode"],
      ]);

      const restorable = new Set ([
         "PrimitiveQuality",
         "Rubberband",
         "StraightenHorizon",
         "TextureQuality",
         "Timings",
      ]);

      return function ()
      {
         const
            browser      = this .getBrowser (),
            localStorage = this .localStorage;

         for (const fieldDefinition of this .getFieldDefinitions ())
         {
            const
               name  = fieldDefinition .getName (),
               value = fieldDefinition .getValue ();

            if (attributes .has (name))
            {
               const
                  attribute = mappings .get (name) ?? BrowserOptions_$.toLowerCaseFirst (name),
                  value     = browser .getElement () .attr (attribute);

               if (value !== undefined)
               {
                  browser .attributeChangedCallback (attribute, null, value);
                  continue;
               }
            }

            if (restorable .has (name))
            {
               const
                  value = localStorage [name],
                  field = this .getField (name);

               if (value !== field .getValue ())
                  field .setValue (value);

               continue;
            }

            const field = this .getField (name);

            if (field .equals (value))
               continue;

            field .assign (value);
         }
      };
   })(),
   getPrimitiveQuality ()
   {
      return this .primitiveQuality;
   },
   getQualityWhenMoving ()
   {
      const qualityWhenMoving = this ._QualityWhenMoving .getValue () .toUpperCase ();

      return Core_PrimitiveQuality .get (qualityWhenMoving);
   },
   getTextureQuality ()
   {
      return this .textureQuality;
   },
   getShading ()
   {
      return this .shading;
   },
   getTextCompression ()
   {
      switch (this ._TextCompression .getValue () .toUpperCase ())
      {
         default: // CHAR_SPACING
            return Core_TextCompression .CHAR_SPACING;
         case "SCALING":
            return Core_TextCompression .SCALING;
      }
   },
   set_Rubberband__ (rubberband)
   {
      this .localStorage .Rubberband = rubberband .getValue ();
   },
   set_Antialiased__ ()
   {
      this .set_Multisampling__ (this ._Multisampling);
   },
   set_PrimitiveQuality__ (value)
   {
      const
         browser          = this .getBrowser (),
         primitiveQuality = value .getValue () .toUpperCase ();

      this .localStorage .PrimitiveQuality = primitiveQuality;
      this .primitiveQuality               = Core_PrimitiveQuality .get (primitiveQuality) ?? Core_PrimitiveQuality .MEDIUM;

      browser .setPrimitiveQuality2D ?.(this .primitiveQuality);
      browser .setPrimitiveQuality3D ?.(this .primitiveQuality);
   },
   set_TextureQuality__ (value)
   {
      const
         browser        = this .getBrowser (),
         textureQuality = value .getValue () .toUpperCase ();

      this .localStorage .TextureQuality = textureQuality;
      this .textureQuality               = TextureQuality .get (textureQuality) ?? TextureQuality .MEDIUM;

      browser .setTextureQuality ?.(this .textureQuality);
   },
   set_Shading__: (() =>
   {
      const strings = new Map (Object .entries (Core_Shading) .map (entry => entry .reverse ()));

      return function (value)
      {
         const
            browser = this .getBrowser (),
            shading = value .getValue () .toUpperCase () .replace ("POINTSET", "POINT");

         this .shading = Core_Shading .get (shading) ?? Core_Shading .GOURAUD;

         browser .getRenderingProperties () ._Shading = strings .get (this .shading);
         browser .setShading (this .shading);
      };
   })(),
   set_StraightenHorizon__ (straightenHorizon)
   {
      this .localStorage .StraightenHorizon = straightenHorizon .getValue ();

      if (straightenHorizon .getValue ())
         this .getBrowser () .getActiveLayer () ?.straightenView ();
   },
   set_AutoUpdate__ (autoUpdate)
   {
      const
         browser = this .getBrowser (),
         element = browser .getElement () [0];

      const documentEvents = ["visibilitychange"]
         .map (event => `${event}.${this .getTypeName ()}${this .getId ()}`)
         .join (" ");

      BrowserOptions_$(document) .off (documentEvents);

      this .intersectionObserver ?.disconnect ();

      if (!autoUpdate .getValue ())
         return;

      BrowserOptions_$(document) .on (documentEvents, () => this .checkUpdate ());

      this .intersectionObserver ??= new IntersectionObserver (entries =>
      {
         this .isIntersecting = entries .some (entry => entry .isIntersecting);

         this .checkUpdate ();
      });

      this .intersectionObserver .observe (element);
   },
   checkUpdate ()
   {
      if (!this ._AutoUpdate .getValue ())
         return;

      const browser = this .getBrowser ();

      if ((!document .hidden && this .isIntersecting) || browser .getPose ())
      {
         if (!browser .isLive ())
            browser .beginUpdate ();
      }
      else
      {
         if (browser .isLive ())
            browser .endUpdate ();
      }
   },
   set_ContentScale__ (contentScale)
   {
      const browser = this .getBrowser ();

      this .removeUpdateContentScale ?.();

      if (contentScale .getValue () === -1)
         this .updateContentScale ();
      else
         browser .getRenderingProperties () ._ContentScale = Math .max (contentScale .getValue (), 0) || 1;

      browser .reshape ();
   },
   updateContentScale ()
   {
      const
         browser = this .getBrowser (),
         media   = window .matchMedia (`(resolution: ${window .devicePixelRatio}dppx)`),
         update  = this .updateContentScale .bind (this);

      this .removeUpdateContentScale ?.();

      this .removeUpdateContentScale = () =>
      {
         visualViewport .removeEventListener ("resize", update);
         media          .removeEventListener ("change", update);
      };

      visualViewport .addEventListener ("resize", update);
      media          .addEventListener ("change", update);

      browser .reshape ();
   },
   set_DisplayColorSpace (displayColorSpace)
   {
      const
         browser  = this .getBrowser (),
         gl       = browser .getContext (),
         value    = displayColorSpace .getValue () .toLowerCase () .replace (/_/g, "-");

      gl .drawingBufferColorSpace = value;
      gl .unpackColorSpace        = value;
   },
   set_Exposure__ ()
   {
      const
         browser  = this .getBrowser (),
         gl       = browser .getContext (),
         exposure = Math .max (this ._Exposure .getValue (), 0);

      for (const shaderNode of browser .getShaders () .values ())
      {
         gl .useProgram (shaderNode .getProgram ());
         gl .uniform1f (shaderNode .x3d_Exposure, exposure);
      }
   },
   set_LogarithmicDepthBuffer__ (logarithmicDepthBuffer)
   {
      const browser = this .getBrowser ();

      browser .getRenderingProperties () ._LogarithmicDepthBuffer = logarithmicDepthBuffer .getValue ();
   },
   set_Multisampling__ (multisampling)
   {
      const
         browser    = this .getBrowser (),
         maxSamples = browser .getMaxSamples (),
         samples    = Math_Algorithm .clamp (isNaN (multisampling .getValue ()) ? 4 : multisampling .getValue (), 0, maxSamples);

      browser .getRenderingProperties () ._Multisampling = this ._Antialiased .getValue () ? samples : 0;
      browser .getRenderingProperties () ._Antialiased   = samples > 0;

      browser .reshape ();
   },
   set_Mute__ (mute)
   {
      const
         browser      = this .getBrowser (),
         audioContext = browser .getAudioContext ();

      if (mute .getValue ())
         browser .stopAudioElement (audioContext, "suspend");
      else
         browser .startAudioElement (audioContext, "resume");
   },
   set_OrderIndependentTransparency__ ()
   {
      this .getBrowser () .reshape ();
   },
   set_Timings__ (timings)
   {
      this .localStorage .Timings = timings .getValue ();
   },
   set_XRSessionMode__ ()
   {
      this .getBrowser () .xrUpdateButton ();
   },
});

Object .defineProperties (BrowserOptions,
{
   typeName:
   {
      value: "BrowserOptions",
      enumerable: true,
   },
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "SplashScreen",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Dashboard",                    new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Rubberband",                   new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "EnableInlineViewpoints",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Antialiased",                  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "TextureQuality",               new x_ite_Fields .SFString ("MEDIUM")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "PrimitiveQuality",             new x_ite_Fields .SFString ("MEDIUM")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "QualityWhenMoving",            new x_ite_Fields .SFString ("SAME")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Shading",                      new x_ite_Fields .SFString ("GOURAUD")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "MotionBlur",                   new x_ite_Fields .SFBool ()),
         // Additional options:
         // Always update geometries, even if browser is not live.
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "AlwaysUpdateGeometries",       new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "AutoUpdate",                   new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Cache",                        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ColorSpace",                   new x_ite_Fields .SFString ("LINEAR_WHEN_PHYSICAL_MATERIAL")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ContentScale",                 new x_ite_Fields .SFDouble (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ContextMenu",                  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Debug",                        new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Exposure",                     new x_ite_Fields .SFDouble (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "DisplayColorSpace",            new x_ite_Fields .SFString ("SRGB")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Gravity",                      new x_ite_Fields .SFDouble (9.80665)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "LoadUrlObjects",               new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "LogarithmicDepthBuffer",       new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "MaximumFrameRate",             new x_ite_Fields .SFDouble (80)),
         // A string, which is set to the *reference* field of metadata nodes, when they are created.
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "MetadataReference",            new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Multisampling",                new x_ite_Fields .SFInt32 (4)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Mute",                         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Notifications",                new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "OrderIndependentTransparency", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "StraightenHorizon",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "TextCompression",              new x_ite_Fields .SFString ("CHAR_SPACING")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "Timings",                      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ToneMapping",                  new x_ite_Fields .SFString ("NONE")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "WallFriction",                 new x_ite_Fields .SFDouble ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "XRSessionMode",                new x_ite_Fields .SFString ("IMMERSIVE_VR")),
      ]),
      enumerable: true,
   },
});

const BrowserOptions_default_ = BrowserOptions;
;

/* harmony default export */ const Core_BrowserOptions = (Namespace/* default */.A .add ("BrowserOptions", BrowserOptions_default_));
;// ./src/x_ite/Browser/Core/BrowserProperties.js






// https://www.web3d.org/documents/specifications/19775-2/V3.3/Part02/servRef.html#getBrowserProperties

function BrowserProperties (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);
}

Object .setPrototypeOf (BrowserProperties .prototype, Base_X3DBaseNode .prototype);

Object .defineProperties (BrowserProperties,
{
   typeName:
   {
      value: "BrowserProperties",
      enumerable: true,
   },
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ABSTRACT_NODES",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "CONCRETE_NODES",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "EXTERNAL_INTERACTIONS", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "PROTOTYPE_CREATE",      new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "DOM_IMPORT",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "XML_ENCODING",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "CLASSIC_VRML_ENCODING", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "BINARY_ENCODING",       new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const BrowserProperties_default_ = BrowserProperties;
;

/* harmony default export */ const Core_BrowserProperties = (Namespace/* default */.A .add ("BrowserProperties", BrowserProperties_default_));
;// ./src/x_ite/Browser/Core/RenderingProperties.js
/* provided dependency */ var RenderingProperties_$ = __webpack_require__(44).A;






// https://www.web3d.org/documents/specifications/19775-2/V3.3/Part02/servRef.html#getRenderingProperties
// https://www.web3d.org/documents/specifications/19777-1/V3.3/Part1/functions.html#t-FunctionsBrowserObject

function RenderingProperties (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addAlias ("AntiAliased", this ._Antialiased);
}

Object .assign (Object .setPrototypeOf (RenderingProperties .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      const browser = this .getBrowser ();

      this ._MaxTextureSize       = browser .getMaxTextureSize ();
      this ._TextureUnits         = browser .getMaxCombinedTextureUnits ();
      this ._MaxLights            = browser .getMaxLights ();
      this ._ColorDepth           = browser .getColorDepth ();
      this ._TextureMemory        = browser .getTextureMemory ();
      this ._MaxAnisotropicDegree = browser .getMaxAnisotropicDegree ();
      this ._MaxSamples           = browser .getMaxSamples ();

      this .set_pixelsPerPoint__ ();
   },
   set_pixelsPerPoint__ ()
   {
      const
         inches         = RenderingProperties_$("<div></div>") .hide () .css ("height", "10in") .appendTo (RenderingProperties_$("body")),
         pixelsPerPoint = inches .height () / 720 || 1;

      inches .remove ();

      this ._PixelsPerPoint = pixelsPerPoint;
   },
});

Object .defineProperties (RenderingProperties,
{
   typeName:
   {
      value: "RenderingProperties",
      enumerable: true,
   },
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "Shading",                new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "MaxTextureSize",         new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "TextureUnits",           new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "MaxLights",              new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "Antialiased",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "ColorDepth",             new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "TextureMemory",          new x_ite_Fields .SFDouble ()),
         // non-standard fields
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "ContentScale",           new x_ite_Fields .SFDouble (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "LogarithmicDepthBuffer", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "MaxAnisotropicDegree",   new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "MaxSamples",             new x_ite_Fields .SFInt32 (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "Multisampling",          new x_ite_Fields .SFInt32 (4)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "PixelsPerPoint",         new x_ite_Fields .SFDouble (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "XRSession",              new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   }
});

const RenderingProperties_default_ = RenderingProperties;
;

/* harmony default export */ const Core_RenderingProperties = (Namespace/* default */.A .add ("RenderingProperties", RenderingProperties_default_));
;// ./src/x_ite/Browser/Core/Notification.js
/* provided dependency */ var Notification_$ = __webpack_require__(44).A;




Notification_$.fn.textWidth = function (string)
{
   const
      self     = Notification_$(this),
      children = self .children (),
      html     = self .html (),
      span     = Notification_$("<span></span>") .html (html);

   self .html (span);
   const width = span .width ();
   self .empty () .append (children);
   return width;
};

function Notification (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addChildObjects (Base_X3DConstants .inputOutput, "string", new x_ite_Fields .SFString ());
}

Object .assign (Object .setPrototypeOf (Notification .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .element = Notification_$("<div></div>")
         .css ("visibility", "hidden")
         .addClass ("x_ite-private-notification")
         .appendTo (this .getBrowser () .getSurface ())
         .css ("width", 0);

      Notification_$("<span></span>") .appendTo (this .element);

      this ._string .addInterest ("set_string__", this);
   },
   set_string__ ()
   {
      if (! this .getBrowser () .getBrowserOption ("Notifications"))
         return;

      if (this ._string .length === 0)
         return;

      clearTimeout (this .timeoutId);

      this .element .children () .text (this ._string .getValue ());

      this .element .css ({
         visibility: "visible",
         width: this .element .textWidth (),
         transition: "width 300ms ease-in-out",
      });

      this .timeoutId = setTimeout (() =>
      {
         this .element .css ({
            visibility: "hidden",
            width: 0,
            transition: "visibility 0s 300ms, width 300ms ease-in-out",
         });
      },
      5000);
   },
});

Object .defineProperties (Notification,
{
   typeName:
   {
      value: "Notification",
      enumerable: true,
   },
});

const Notification_default_ = Notification;
;

/* harmony default export */ const Core_Notification = (Namespace/* default */.A .add ("Notification", Notification_default_));
;// ./src/x_ite/Browser/Core/ContextMenu.js
/* provided dependency */ var ContextMenu_$ = __webpack_require__(44).A;




const
   _options  = Symbol (),
   _userMenu = Symbol (),
   _hide     = Symbol ();

function ContextMenu (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this [_userMenu] = null;
}

Object .assign (Object .setPrototypeOf (ContextMenu .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      const browser = this .getBrowser ();

      this [_options] = {
         element: browser .getElement (),
         appendTo: browser .getShadow (),
         build: this .build .bind (this),
      };

      this [_options] .element .on ("contextmenu.ContextMenu", event => this .show (event));
   },
   getUserMenu ()
   {
      return this [_userMenu];
   },
   setUserMenu (userMenu)
   {
      this [_userMenu] = userMenu;
   },
   createUserMenu ()
   {
      const userMenu = { };

      if (typeof this [_userMenu] === "function")
      {
         const menu = ContextMenu_$.try (() => this [_userMenu] (this .getBrowser ()), true);

         if (ContextMenu_$.isPlainObject (menu))
         {
            for (const key in menu)
               userMenu [`user-${key}`] = menu [key];
         }
      }

      return userMenu;
   },
   hide ()
   {
      this [_hide] ?.();
   },
   show (event)
   {
      const
         options = this [_options],
         menu    = options .build (event),
         level   = 1;

      this .hide ();

      if (!menu) return;

      // Layer

      const layer = ContextMenu_$("<div></div>")
         .addClass (["context-menu-layer", menu .className])
         .appendTo (options .appendTo);

      this [_hide] = () =>
      {
         this [_hide] = null;

         layer .remove ();
         ul .children ()
            .removeClass ("x_ite-private-fade-in-300")
            .addClass ("x_ite-private-fade-out-300");

         setTimeout (() => ul .remove (), 1000);

         return false;
      };

      // Menu

      const ul = ContextMenu_$("<ul></ul>")
         .hide ()
         .addClass (["context-menu-root", "context-menu-list", menu .className])
         .offset ({ "left": event .pageX, "top": event .pageY })
         .appendTo (options .appendTo);

      ContextMenu_$("<div></div>")
         .addClass ("context-menu-background")
         .appendTo (ul);

      for (const k in menu .items)
         ul .append (this .createItem (menu .items [k], "context-menu-root", k, level + 1));

      // Show
      // Must animate children because of blurish background.

      ul .children () .addClass ("x_ite-private-hidden");
      ul .show ();
      ul .children ()
         .removeClass ("x_ite-private-hidden")
         .addClass ("x_ite-private-fade-in-300");

      // Reposition menu if to right or to low.

      ul .offset ({ "left": event .pageX, "top": event .pageY }); // Do it again!

      if (ul .offset () .left - ContextMenu_$(document) .scrollLeft () + ul .outerWidth () > ContextMenu_$(window) .width ())
         ul .offset ({ "left":  ContextMenu_$(document) .scrollLeft () + Math .max (0, ContextMenu_$(window) .width () - ul .outerWidth ()) });

      if (ul .offset () .top - ContextMenu_$(document) .scrollTop () + ul .outerHeight () > ContextMenu_$(window) .height ())
         ul .offset ({ "top": ContextMenu_$(document) .scrollTop () + Math .max (0, ContextMenu_$(window) .height () - ul .outerHeight ()) });

      // Display submenus on the left or right side.
      // If the submenu is higher than vh, add scrollbars.

      ul .find ("ul") .each ((i, e) =>
      {
         e = ContextMenu_$(e);

         const
            width    = e .outerWidth () + ul .outerWidth (),
            position = ul .offset () .left - ContextMenu_$(document) .scrollLeft () + width > ContextMenu_$(window) .width () ? "right" : "left";

         e .children (":first-child") .css ("height", e .innerHeight ());

         e
            .css ("width",  e .outerWidth ())
            .css (position, e .parent () .closest ("ul") .width () - 12);

         if (e .outerHeight () >= ContextMenu_$(window) .height ())
            e .css ({ "max-height": "100vh", "overflow-y": "scroll" });
      });

      // If the submenu is higher than vh, reposition it.

      ul .find ("li") .on ("mouseenter touchstart", event =>
      {
         event .stopImmediatePropagation ();

         const
            t = ContextMenu_$(event .target) .closest ("li"),
            e = t .children ("ul");

         if (!e .length)
            return;

         e .css ("top", "");

         const bottom = e .offset () .top + e .outerHeight () - ContextMenu_$(window) .scrollTop () - ContextMenu_$(window) .height ();

         if (bottom > 0)
            e .offset ({ "top": e .offset () .top - bottom });
      });

      // Layer

      layer .on ("mousedown contextmenu", () => this .hide ());
      ul .on ("contextmenu", () => this .hide ());

      return false;
   },
   createItem (item, parent, key, level)
   {
      const li = ContextMenu_$("<li></li>") .addClass ("context-menu-item");

      switch (typeof item)
      {
         case "string":
         {
            if (item .match (/^-+$/))
               li .addClass (["context-menu-separator", "context-menu-not-selectable"]);

            break;
         }
         case "object":
         {
            if (item .className)
               li .addClass (item .className);

            switch (item .type)
            {
               case "radio":
               case "checkbox":
               {
                  const
                     label = ContextMenu_$("<label></label>") .appendTo (li),
                     input = ContextMenu_$("<input></input>") .appendTo (label);

                  input
                     .attr ("type", item .type)
                     .attr ("name", `context-menu-input-${item .radio || parent}`);

                  ContextMenu_$("<span></span>") .text (item .name) .appendTo (label);

                  if (item .selected)
                     input .attr ("checked", "");

                  li .addClass ("context-menu-input");
                  this .addEvents (item, input, false);
                  break;
               }
               default:
               {
                  if (item .name)
                     ContextMenu_$("<span></span>") .text (item .name) .appendTo (li);

                  this .addEvents (item, li, true);
                  break;
               }
            }

            break;
         }
      }

      if (typeof item .items === "object" && level < 3)
      {
         const ul = ContextMenu_$("<ul></ul>")
            .addClass ("context-menu-list")
            .css ("z-index", level)
            .appendTo (li);

         ContextMenu_$("<div></div>")
            .addClass ("context-menu-background")
            .appendTo (ul);

         for (const k in item .items)
            ul .append (this .createItem (item .items [k], key, k, level + 1));

         li .addClass ("context-menu-submenu");
      }

      return li;
   },
   addEvents (item, element, hide)
   {
      if (typeof item .callback === "function")
      {
         element .on ("click", item .callback);

         if (hide)
            element .on ("click", () => this .hide ());
      }

      if (typeof item .events === "object")
      {
         for (const k in item .events)
         {
            if (typeof item .events [k] === "function")
               element .on (k, item .events [k]);
         }
      }
   },
   build (event)
   {
      const
         browser    = this .getBrowser (),
         element    = browser .getElement (),
         fullscreen = document .fullscreenElement === element [0];

      if (!browser .getBrowserOption ("ContextMenu"))
         return;

      const menu = {
         className: "x_ite-private-menu",
         items: {
            "title": {
               name: `${browser .getName ()} Browser v${browser .getVersion ()}`,
               className: "context-menu-title context-menu-icon x_ite-private-icon-logo context-menu-not-selectable",
            },
            "separator0": "--------",
            "viewpoints": {
               name: gettext("Viewpoints"),
               className: "context-menu-icon x_ite-private-icon-viewpoint",
               items: this .getViewpoints (),
            },
            "available-viewers": {
               name: gettext("Available Viewers"),
               className: "context-menu-icon x_ite-private-icon-viewer",
               items: this .getAvailableViewers (),
            },
            "straighten-horizon": {
               name: gettext("Straighten Horizon"),
               type: "checkbox",
               selected: browser .getBrowserOption ("StraightenHorizon"),
               callback: (event) =>
               {
                  const straightenHorizon = ContextMenu_$(event .target) .is (":checked");

                  browser .setBrowserOption ("StraightenHorizon", straightenHorizon);

                  if (straightenHorizon)
                     browser .setDescription (gettext("Straighten Horizon") + ": " + gettext("on"));
                  else
                     browser .setDescription (gettext("Straighten Horizon") + ": " + gettext("off"));
               },
            },
            "display-rubberband": {
               name: gettext("Display Rubberband"),
               type: "checkbox",
               selected: browser .getBrowserOption ("Rubberband"),
               callback: (event) =>
               {
                  const rubberband = ContextMenu_$(event .target) .is (":checked");

                  browser .setBrowserOption ("Rubberband", rubberband);

                  if (rubberband)
                     browser .setDescription (gettext("Rubberband") + ": " + gettext("on"));
                  else
                     browser .setDescription (gettext("Rubberband") + ": " + gettext("off"));
               },
            },
            "separator1": "--------",
            "primitive-quality": {
               name: gettext("Primitive Quality"),
               className: "context-menu-icon x_ite-private-icon-primitive-quality",
               items: {
                  "high": {
                     name: gettext("High"),
                     type: "radio",
                     radio: "primitive-quality",
                     selected: browser .getBrowserOption ("PrimitiveQuality") === "HIGH",
                     callback: () =>
                     {
                        browser .setBrowserOption ("PrimitiveQuality", "HIGH");
                        browser .setDescription (gettext("Primitive Quality") + ": " + gettext("high"));
                     },
                  },
                  "medium": {
                     name: gettext("Medium"),
                     type: "radio",
                     radio: "primitive-quality",
                     selected: browser .getBrowserOption ("PrimitiveQuality") === "MEDIUM",
                     callback: () =>
                     {
                        browser .setBrowserOption ("PrimitiveQuality", "MEDIUM");
                        browser .setDescription (gettext("Primitive Quality") + ": " + gettext("medium"));
                     },
                  },
                  "low": {
                     name: gettext("Low"),
                     type: "radio",
                     radio: "primitive-quality",
                     selected: browser .getBrowserOption ("PrimitiveQuality") === "LOW",
                     callback: () =>
                     {
                        browser .setBrowserOption ("PrimitiveQuality", "LOW");
                        browser .setDescription (gettext("Primitive Quality") + ": " + gettext("low"));
                     },
                  },
               },
            },
            "texture-quality": {
               name: gettext("Texture Quality"),
               className: "context-menu-icon x_ite-private-icon-texture-quality",
               items: {
                  "high": {
                     name: gettext("High"),
                     type: "radio",
                     radio: "texture-quality",
                     selected: browser .getBrowserOption ("TextureQuality") === "HIGH",
                     callback: () =>
                     {
                        browser .setBrowserOption ("TextureQuality", "HIGH");
                        browser .setDescription (gettext("Texture Quality") + ": " + gettext("high"));
                     },
                  },
                  "medium": {
                     name: gettext("Medium"),
                     type: "radio",
                     radio: "texture-quality",
                     selected: browser .getBrowserOption ("TextureQuality") === "MEDIUM",
                     callback: () =>
                     {
                        browser .setBrowserOption ("TextureQuality", "MEDIUM");
                        browser .setDescription (gettext("Texture Quality") + ": " + gettext("medium"));
                     },
                  },
                  "low": {
                     name: gettext("Low"),
                     type: "radio",
                     radio: "texture-quality",
                     selected: browser .getBrowserOption ("TextureQuality") === "LOW",
                     callback: () =>
                     {
                        browser .setBrowserOption ("TextureQuality", "LOW");
                        browser .setDescription (gettext("Texture Quality") + ": " + gettext("low"));
                     },
                  },
               },
            },
            "shading": {
               name: gettext("Shading"),
               className: "context-menu-icon x_ite-private-icon-shading",
               items: {
                  "point": {
                     name: gettext("Points"),
                     type: "radio",
                     radio: "shading",
                     selected: browser .getBrowserOption ("Shading") === "POINT",
                     callback: () =>
                     {
                        browser .setBrowserOption ("Shading", "POINT");
                        browser .setDescription (gettext("Shading") + ": " + gettext("Points"));
                     },
                  },
                  "wireframe": {
                     name: gettext("Wireframe"),
                     type: "radio",
                     radio: "shading",
                     selected: browser .getBrowserOption ("Shading") === "WIREFRAME",
                     callback: () =>
                     {
                        browser .setBrowserOption ("Shading", "WIREFRAME");
                        browser .setDescription (gettext("Shading") + ": " + gettext("Wireframe"));
                     },
                  },
                  "flat": {
                     name: gettext("Flat"),
                     type: "radio",
                     radio: "shading",
                     selected: browser .getBrowserOption ("Shading") === "FLAT",
                     callback: () =>
                     {
                        browser .setBrowserOption ("Shading", "FLAT");
                        browser .setDescription (gettext("Shading") + ": " + gettext("Flat"));
                     },
                  },
                  "gouraud": {
                     name: gettext("Gouraud"),
                     type: "radio",
                     radio: "shading",
                     selected: browser .getBrowserOption ("Shading") === "GOURAUD",
                     callback: () =>
                     {
                        browser .setBrowserOption ("Shading", "GOURAUD");
                        browser .setDescription (gettext("Shading") + ": " + gettext("Gouraud"));
                     },
                  },
                  "phong": {
                     name: gettext("Phong"),
                     type: "radio",
                     radio: "shading",
                     selected: browser .getBrowserOption ("Shading") === "PHONG",
                     callback: () =>
                     {
                        browser .setBrowserOption ("Shading", "PHONG");
                        browser .setDescription (gettext("Shading") + ": " + gettext("Phong"));
                     },
                  },
               },
            },
            "separator2": "--------",

            // Insert custom user menu items.
            ... this .createUserMenu (),

            "separator3": "--------",

            ... this .createAnimationsMenu (),

            "browser-timings": {
               name: gettext("Browser Timings"),
               type: "checkbox",
               selected: browser .getBrowserOption ("Timings"),
               callback: (event) =>
               {
                  browser .setBrowserOption ("Timings", ContextMenu_$(event .target) .is (":checked"));
                  browser .getSurface () .focus ();
               },
            },
            "fullscreen": {
               name: fullscreen ? gettext("Leave Fullscreen") : gettext("Fullscreen"),
               className: `context-menu-icon ${fullscreen ? "x_ite-private-icon-leave-fullscreen" : "x_ite-private-icon-enter-fullscreen"}`,
               callback: () =>
               {
                  if (fullscreen)
                     document .exitFullscreen () .catch (Function .prototype);
                  else
                     element [0] .requestFullscreen ({ navigationUI: "hide" }) .catch (Function .prototype);
               },
            },
            "separator4": "--------",
            "world-info": {
               name: gettext("Show World Info"),
               className: "context-menu-icon x_ite-private-icon-world-info",
               callback ()
               {
                  browser .getShadow () .find (".x_ite-private-world-info") .remove ();

                  const
                     priv         = browser .getShadow () .find (".x_ite-private-browser"),
                     overlay      = ContextMenu_$("<div></div>") .addClass ("x_ite-private-world-info-overlay") .appendTo (priv),
                     div          = ContextMenu_$("<div></div>") .hide () .addClass (["x_ite-private-world-info", "x_ite-private-hidden"]) .appendTo (overlay),
                     content      = ContextMenu_$("<div></div>"),
                     worldInfos   = browser .getExecutionContext () .getWorldInfos (),
                     worldInfo    = worldInfos .length ? worldInfos [0] : null,
                     hasWorldInfo = worldInfo ?.title .length || worldInfo ?.info .length;

                  const linkify = function ()
                  {
                     const email = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9]{2,})/ig;

                     const link = /(https?:\/\/[-a-zA-Z0-9@:%._\+~#=]+\.[a-zA-Z0-9]{2,}\b[-a-zA-Z0-9()@:%_\+.~#?&\/=]*\b\/?)/ig;

                     const replaced_text = ContextMenu_$(this) .html ()
                        .replace (email, `<a href="mailto:$1" target="_blank">$1</a>`)
                        .replace (link, `<a href="$1" target="_blank">$1</a>`);

                     ContextMenu_$(this)
                        .html (replaced_text)
                        .find ("a")
                        .on ("click", event => event .stopPropagation ());
                  };

                  const buttons = ContextMenu_$("<div></div>")
                     .addClass ("x_ite-private-world-info-buttons")
                     .appendTo (div);

                  const worldInfoButton = ContextMenu_$("<button></button>")
                     .addClass ("x_ite-private-browser-button")
                     .css ("width", "100pt")
                     .text ("World Info")
                     .on ("click", event =>
                     {
                        event .preventDefault ();
                        event .stopPropagation ();
                        event .stopImmediatePropagation ();

                        const
                           title = worldInfo .title,
                           info  = worldInfo .info;

                        buttons .find ("button") .removeClass ("active");
                        worldInfoButton .addClass ("active");

                        content .empty ();

                        ContextMenu_$("<div></div>")
                           .addClass ("x_ite-private-world-info-top")
                           .text ("World Info")
                           .appendTo (content);

                        if (title .length)
                        {
                           ContextMenu_$("<div></div>")
                              .addClass ("x_ite-private-world-info-title")
                              .text (title)
                              .appendTo (content);
                        }

                        for (const line of info)
                        {
                           ContextMenu_$("<div></div>")
                              .addClass ("x_ite-private-world-info-info")
                              .text (line)
                              .appendTo (content)
                              .each (linkify);
                        }
                     })
                     .appendTo (buttons);

                  const metaDataButton = ContextMenu_$("<button></button>")
                     .addClass ("x_ite-private-browser-button")
                     .css ("width", "100pt")
                     .text ("Metadata")
                     .on ("click", event =>
                     {
                        event .preventDefault ();
                        event .stopPropagation ();
                        event .stopImmediatePropagation ();

                        buttons .find ("button") .removeClass ("active");
                        metaDataButton .addClass ("active");

                        content .empty ();

                        ContextMenu_$("<div></div>")
                           .addClass ("x_ite-private-world-info-top")
                           .text ("Metadata")
                           .appendTo (content);

                        const table = ContextMenu_$("<table></table>") .appendTo (content);

                        for (const [key, value] of browser .currentScene .getMetaDatas ())
                        {
                           const tr = ContextMenu_$("<tr></tr>") .appendTo (table);

                           ContextMenu_$("<td></td>") .text (key + ":") .appendTo (tr);
                           ContextMenu_$("<td></td>")
                              .text (value)
                              .appendTo (tr)
                              .each (linkify);
                        }

                     })
                     .appendTo (buttons);

                  content .appendTo (div);

                  if (!hasWorldInfo)
                     worldInfoButton .hide ();

                  if (!browser .currentScene .getMetaDatas () .length)
                     metaDataButton .hide ();

                  if (hasWorldInfo)
                     worldInfoButton .trigger ("click");
                  else
                     metaDataButton .trigger ("click");

                  div
                     .show ()
                     .removeClass ("x_ite-private-hidden")
                     .addClass ("x_ite-private-fade-in-300");

                  overlay .on ("click", () =>
                  {
                     div .addClass ("x_ite-private-fade-out-300");
                     setTimeout (() => overlay .remove (), 300);
                  });
               },
            },
            "about": {
               name: gettext("About X_ITE"),
               className: "context-menu-icon x_ite-private-icon-info",
               callback ()
               {
                  window .open (browser .getProviderURL ());
               },
            },
         },
      };

      if (ContextMenu_$.isEmptyObject (menu .items .viewpoints .items))
         delete menu .items ["viewpoints"];

      if (Object .keys (menu .items ["available-viewers"] .items) .length < 2)
      {
         delete menu .items ["available-viewers"];
      }

      if (!browser .getCurrentViewer () .match (/^(?:EXAMINE|FLY)$/))
      {
         delete menu .items ["straighten-horizon"];
      }

      if (!browser .getCurrentViewer () .match (/^(?:WALK|FLY)$/))
      {
         delete menu .items ["display-rubberband"];
      }

      if (!browser .getBrowserOption ("Debug"))
      {
         delete menu .items ["shading"];
      }

      const worldInfo = browser .getExecutionContext () .getWorldInfos () [0];

      if ((!worldInfo || (worldInfo .title .length === 0 && worldInfo .info .length === 0)) && !browser .currentScene .getMetaDatas () .length)
      {
         delete menu .items ["world-info"];
      }

      return menu;
   },
   getViewpoints ()
   {
      const
         browser     = this .getBrowser (),
         activeLayer = browser .getActiveLayer ();

      if (!activeLayer)
         return { };

      const
         viewpoints       = activeLayer .getUserViewpoints (),
         currentViewpoint = activeLayer .getViewpoint (),
         menu             = { };

      for (const viewpoint of viewpoints)
      {
         menu [`Viewpoint-${viewpoint .getId ()}`] = {
            name: viewpoint .getDescriptions () .join (" » "),
            type: "radio",
            radio: "viewpoints",
            selected: viewpoint === currentViewpoint,
            className: "x_ite-private-viewpoint",
            callback: () =>
            {
               browser .bindViewpoint (browser .getActiveLayer (), viewpoint);
               browser .getSurface () .focus ();
            },
         };
      }

      return menu;
   },
   getAvailableViewers ()
   {
      const
         browser          = this .getBrowser (),
         currentViewer    = browser ._viewer .getValue (),
         availableViewers = browser ._availableViewers,
         menu             = { };

      for (const viewer of availableViewers)
      {
         menu [viewer] = {
            name: gettext(this .getViewerName (viewer)),
            type: "radio",
            radio: "viewers",
            selected: viewer === currentViewer,
            className: `x_ite-private-${viewer .toLowerCase ()}-viewer`,
            callback: () =>
            {
               browser ._viewer = viewer;
               browser .setDescription (gettext(this .getViewerName (viewer)));
               browser .getSurface () .focus ();
            },
         };
      }

      return menu;
   },
   getViewerName (viewer)
   {
      switch (viewer)
      {
         case "EXAMINE":
            return gettext("Examine Viewer");
         case "WALK":
            return gettext("Walk Viewer");
         case "FLY":
            return gettext("Fly Viewer");
         case "PLANE":
            return gettext("Plane Viewer");
         case "LOOKAT":
            return gettext("Look At Viewer");
         case "NONE":
            return gettext("None Viewer");
      }
   },
   createAnimationsMenu ()
   {
      const
         browser = this .getBrowser (),
         scene   = browser .currentScene;

      const animations = ContextMenu_$.try (() => scene .getExportedNode ("Animations"));

      if (!animations)
         return { };

      const timeSensors = ContextMenu_$.try (() => Array .from (animations .children, group => group .children [0]))
         .filter (node => node .getNodeType () .includes (Base_X3DConstants .TimeSensor));

      if (!timeSensors ?.length)
         return { };

      return {
         "animations": {
            name: gettext("Animations"),
            className: "context-menu-icon x_ite-private-icon-animations",
            items: {
               ... Object .fromEntries (timeSensors .map ((timeSensor, i) =>
               {
                  return [`animation-${i}`, {
                     name: timeSensor .description,
                     type: "checkbox",
                     selected: timeSensor .isActive,
                     callback ()
                     {
                        if (timeSensor .isActive)
                        {
                           timeSensor .stopTime = Date .now () / 1000;
                        }
                        else
                        {
                           timeSensor .loop      = true;
                           timeSensor .startTime = Date .now () / 1000;
                        }
                     },
                  }];
               })),

               "separator2": "--------",

               "all": {
                  name: gettext("All"),
                  className: "context-menu-icon x_ite-private-icon-play",
                  callback ()
                  {
                     for (const timeSensor of timeSensors)
                     {
                        timeSensor .loop      = true;
                        timeSensor .startTime = Date .now () / 1000;
                     }
                  },
               },
               "none": {
                  name: gettext("None"),
                  className: "context-menu-icon x_ite-private-icon-stop",
                  callback ()
                  {
                     for (const timeSensor of timeSensors)
                        timeSensor .stopTime = Date .now () / 1000;
                  },
               },
            },
         },
      };
   }
});

Object .defineProperties (ContextMenu .prototype,
{
   userMenu:
   {
      get: ContextMenu .prototype .getUserMenu,
      set: ContextMenu .prototype .setUserMenu,
      enumerable: true,
   },
});

Object .defineProperties (ContextMenu,
{
   typeName:
   {
      value: "ContextMenu",
      enumerable: true,
   },
});

const ContextMenu_default_ = ContextMenu;
;

/* harmony default export */ const Core_ContextMenu = (Namespace/* default */.A .add ("ContextMenu", ContextMenu_default_));
;// ./src/x_ite/Browser/Networking/URLs.js



const URLs =
{
   getScriptURL: (() =>
   {
      let src;

      switch (x_ite_Features .ENVIRONMENT)
      {
         case "NODE":
         {
            src = __webpack_require__.g .require ("url") .pathToFileURL (__filename) .href;
            break;
         }
         case "BROWSER":
         {
            src = document .currentScript ?.src ?? document .location .href;
            break;
         }
         case "MODULE":
         {
            src = import .meta .url;
            break;
         }
      }

      // Prevent caching issues with jsDelivr and UNPKG.
      src = src .replace ("/x_ite@latest/", `/x_ite@${BROWSER_VERSION}/`);

      return function ()
      {
         return src;
      };
   })(),
   getProviderURL (component)
   {
      if (!component)
         return "https://create3000.github.io/x_ite/";

      const min = this .getScriptURL () .match (/\.min\.m?js$/) ? ".min" : "";

      return new URL (`assets/components/${component}Component${min}.js`, this .getScriptURL ()) .href;
   },
   getFontsURL (file)
   {
      return new URL (`assets/fonts/${file}`, this .getScriptURL ()) .href;
   },
   getLinetypeURL ()
   {
      return new URL ("assets/linetype/linetypes.png", this .getScriptURL ()) .href;
   },
   getHatchingURL (index)
   {
      return new URL (`assets/hatching/${index}.png`, this .getScriptURL ()) .href;
   },
   getLibraryURL (file)
   {
      return new URL (`assets/lib/${file}`, this .getScriptURL ()) .href;
   },
};

const URLs_default_ = URLs;
;

/* harmony default export */ const Networking_URLs = (Namespace/* default */.A .add ("URLs", URLs_default_));
;// ./src/x_ite/Components/Core/X3DNode.js






const _metaDataCallbacks = Symbol ();

function X3DNode (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DNode);
}

Object .assign (Object .setPrototypeOf (X3DNode .prototype, Base_X3DBaseNode .prototype),
{
   getComponentInfo ()
   {
      return this .constructor .componentInfo;
   },
   getContainerField ()
   {
      return this .constructor .containerField;
   },
   getSpecificationRange ()
   {
      return this .constructor .specificationRange;
   },
   create (executionContext = this .getExecutionContext ())
   {
      return new (this .constructor) (executionContext);
   },
   copy (instance)
   {
      if (instance ?.getType () .includes (Base_X3DConstants .X3DExecutionContext) ?? true)
      {
         const copy = this .create (instance);

         for (const field of this .getPredefinedFields ())
            copy .getPredefinedFields () .get (field .getName ()) .assign (field);

         if (this .canUserDefinedFields ())
         {
            for (const field of this .getUserDefinedFields ())
               copy .addUserDefinedField (field .getAccessType (), field .getName (), field .copy ());
         }

         return copy;
      }
      else
      {
         const executionContext = instance .getBody ();

         // First try to get a named node with the node's name.

         if (this .getName () .length)
         {
            const namedNode = executionContext .getNamedNodes () .get (this .getName ());

            if (namedNode)
               return namedNode .getValue ();
         }

         // Create copy.

         const copy = this .create (executionContext);

         // Support editing of protos.
         if (this .getNeedsName ())
            this .getExecutionContext () .updateNamedNode (this .getExecutionContext () .getUniqueName (), this);

         if (this .getName () .length)
            executionContext .updateNamedNode (this .getName (), copy);

         // Default fields

         for (const sourceField of this .getPredefinedFields ())
         {
            try
            {
               const destinationField = copy .getPredefinedField (sourceField .getName ());

               let initializableReferences = false;

               for (const originalReference of sourceField .getReferences ())
                  initializableReferences ||= originalReference .isInitializable ();

               if (sourceField .isInitializable () && !initializableReferences)
               {
                  switch (sourceField .getType ())
                  {
                     case Base_X3DConstants .SFNode:
                     case Base_X3DConstants .MFNode:
                        destinationField .assign (sourceField .copy (instance));
                        break;
                     default:
                        destinationField .assign (sourceField);
                        break;
                  }
               }

               if (sourceField .getReferences () .size)
               {
                  // IS relationship

                  for (const originalReference of sourceField .getReferences ())
                  {
                     try
                     {
                        destinationField .addReference (instance .getField (originalReference .getName ()));
                     }
                     catch (error)
                     {
                        console .error (error);
                     }
                  }
               }

               destinationField .setModificationTime (sourceField .getModificationTime ());
            }
            catch (error)
            {
               console .error (error);
            }
         }

         // User-defined fields

         for (const sourceField of this .getUserDefinedFields ())
         {
            let initializableReferences = false;

            for (const originalReference of sourceField .getReferences ())
               initializableReferences ||= originalReference .isInitializable ();

            const destinationField = initializableReferences
               ? sourceField .create ()
               : sourceField .copy (instance);

            copy .addUserDefinedField (sourceField .getAccessType (),
                                       sourceField .getName (),
                                       destinationField);

            if (sourceField .getReferences () .size)
            {
               // IS relationship

               for (const originalReference of sourceField .getReferences ())
               {
                  try
                  {
                     destinationField .addReference (instance .getField (originalReference .getName ()));
                  }
                  catch (error)
                  {
                     console .error (`No reference '${originalReference .getName ()}' inside execution context ${instance .getTypeName ()} '${instance .getName ()}'.`);
                  }
               }
            }

            destinationField .setModificationTime (sourceField .getModificationTime ());
         }

         return copy;
      }
   },
   getDisplayName ()
   {
      return this .getName () .replace (/_\d+$/, "");
   },
   getNeedsName ()
   {
      if (this .getName () .length)
         return false;

      if (this .getCloneCount () > 1)
         return true;

      if (this .hasRoutes ())
         return true;

      const executionContext = this .getExecutionContext ()

      for (const importedNode of executionContext .getImportedNodes ())
      {
         if (importedNode .getInlineNode () === this)
            return true;
      }

      if (executionContext .isScene)
      {
         for (const exportedNode of executionContext .getExportedNodes ())
         {
            if (exportedNode .getLocalNode () === this)
               return true;
         }
      }

      return false;
   },
   hasRoutes ()
   {
      ///  Returns true if there are any routes from or to fields of this node, otherwise false.

      for (const field of this .getPredefinedFields ())
      {
         if (field .getInputRoutes () .size || field .getOutputRoutes () .size)
            return true;
      }

      for (const field of this .getUserDefinedFields ())
      {
         if (field .getInputRoutes () .size || field .getOutputRoutes () .size)
            return true;
      }

      for (const route of this .getExecutionContext () .getRoutes ())
      {
         if (route .getSourceNode () === this || route .getDestinationNode () === this)
            return true;
      }

      return false;
   },
   getCloneCount ()
   {
      return Base_X3DBaseNode .prototype .collectCloneCount .call (this);
   },
   collectCloneCount ()
   {
      return 1;
   },
   getSourceText ()
   {
      return null;
   },
   traverse ()
   { },
   hasMetaData (path)
   {
      const names = path .split ("/");

      if (names .length < 2)
         return false;

      const
         last           = names .pop (),
         metadataSet    = this .getMetadataSet (names),
         metadataObject = metadataSet ?.getValue () .getMetaValue (last);

      return !!metadataObject;
   },
   getMetaData (path, field)
   {
      const names = path .split ("/");

      if (names .length < 2)
         return;

      const
         last           = names .pop (),
         metadataSet    = this .getMetadataSet (names),
         metadataObject = metadataSet ?.getValue () .getMetaValue (last);

      if (field instanceof Base_X3DField)
      {
         if (!metadataObject)
            return field;

         switch (field .getType ())
         {
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .SFDouble:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFInt32:
            case Base_X3DConstants .SFString:
            case Base_X3DConstants .SFTime:
            {
               field .setValue (metadataObject .value [0]);
               break;
            }
            case Base_X3DConstants .SFColor:
            case Base_X3DConstants .SFColorRGBA:
            case Base_X3DConstants .SFMatrix3d:
            case Base_X3DConstants .SFMatrix3f:
            case Base_X3DConstants .SFMatrix4d:
            case Base_X3DConstants .SFMatrix4f:
            case Base_X3DConstants .SFRotation:
            case Base_X3DConstants .SFVec2d:
            case Base_X3DConstants .SFVec2f:
            case Base_X3DConstants .SFVec3d:
            case Base_X3DConstants .SFVec3f:
            case Base_X3DConstants .SFVec4d:
            case Base_X3DConstants .SFVec4f:
            {
               const value = metadataObject .value;

               let i = 0;

               for (const key in field)
                  field [key] = value [i ++];

               break;
            }
            case Base_X3DConstants .SFImage:
            {
               const
                  value = metadataObject .value,
                  array = field .array;

               field .width  = value [0];
               field .height = value [1];
               field .comp   = value [2];

               const l = array .length;

               for (let i = 0; i < l; ++ i)
                  array [i] = value [3 + i];

               break;
            }
            case Base_X3DConstants .SFNode:
            case Base_X3DConstants .MFNode:
            {
               throw new Error ("SFNode and MFNode are not supported as metadata value.");
            }
            case Base_X3DConstants .MFBool:
            case Base_X3DConstants .MFDouble:
            case Base_X3DConstants .MFFloat:
            case Base_X3DConstants .MFInt32:
            case Base_X3DConstants .MFString:
            case Base_X3DConstants .MFTime:
            {
               field .length = 0;

               for (const v of metadataObject .value)
                  field .push (v);

               break;
            }
            case Base_X3DConstants .MFColor:
            case Base_X3DConstants .MFColorRGBA:
            case Base_X3DConstants .MFMatrix3d:
            case Base_X3DConstants .MFMatrix3f:
            case Base_X3DConstants .MFMatrix4d:
            case Base_X3DConstants .MFMatrix4f:
            case Base_X3DConstants .MFRotation:
            case Base_X3DConstants .MFVec2d:
            case Base_X3DConstants .MFVec2f:
            case Base_X3DConstants .MFVec3d:
            case Base_X3DConstants .MFVec3f:
            case Base_X3DConstants .MFVec4d:
            case Base_X3DConstants .MFVec4f:
            {
               const
                  value  = metadataObject .value,
                  length = value .length;

               field .length = 0;

               for (let i = 0; i < length;)
               {
                  const
                     l = field .length ++,
                     f = field [l];

                  for (const key in f)
                     f [key] = value [i ++];
               }

               break;
            }
            case Base_X3DConstants .MFImage:
            {
               const
                  value  = metadataObject .value,
                  length = value .length;

               field .length = 0;

               for (let i = 0; i < length;)
               {
                  const
                     l = field .length ++,
                     f = field [l],
                     a = f .array;

                  f .width  = value [i ++];
                  f .height = value [i ++];
                  f .comp   = value [i ++];

                  const p = a .length;

                  for (let k = 0; k < p; ++ k)
                     a [k] = value [i ++];
               }

               break;
            }
         }

         return field;
      }
      else if (field)
      {
         // Support for Numbers (Vector234, Color34, ...).

         if (!metadataObject)
            return field;

         const value = metadataObject .value;

         let i = 0;

         for (const key in field)
            field [key] = value [i ++];

         return field;
      }

      return metadataObject ? Array .from (metadataObject .value) : [ ];
   },
   setMetaData (path, value)
   {
      const names = path .split ("/");

      if (names .length < 2)
         return;

      const
         last        = names .pop (),
         metadataSet = this .getMetadataSet (names, true);

      if (value instanceof Base_X3DField)
      {
         const field = value;

         switch (value .getType ())
         {
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .SFDouble:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFInt32:
            case Base_X3DConstants .SFString:
            case Base_X3DConstants .SFTime:
            {
               value = [field .valueOf ()];
               break;
            }
            case Base_X3DConstants .SFNode:
            case Base_X3DConstants .MFNode:
               throw new Error ("SFNode and MFNode are not supported as metadata value.");
            default:
            {
               value = Array .from (field, f => f instanceof Base_X3DField ? Array .from (f) : f) .flat ();
               break;
            }
         }

         switch (field .getType ())
         {
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .MFBool:
               value .type = "boolean";
               break;
            case Base_X3DConstants .SFDouble:
            case Base_X3DConstants .SFMatrix3d:
            case Base_X3DConstants .SFMatrix4d:
            case Base_X3DConstants .SFRotation:
            case Base_X3DConstants .SFTime:
            case Base_X3DConstants .SFVec2d:
            case Base_X3DConstants .SFVec3d:
            case Base_X3DConstants .SFVec4d:
            case Base_X3DConstants .MFDouble:
            case Base_X3DConstants .MFMatrix3d:
            case Base_X3DConstants .MFMatrix4d:
            case Base_X3DConstants .MFRotation:
            case Base_X3DConstants .MFTime:
            case Base_X3DConstants .MFVec2d:
            case Base_X3DConstants .MFVec3d:
            case Base_X3DConstants .MFVec4d:
               value .type = "double";
               break;
            case Base_X3DConstants .SFColor:
            case Base_X3DConstants .SFColorRGBA:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFMatrix3f:
            case Base_X3DConstants .SFMatrix4f:
            case Base_X3DConstants .SFVec2f:
            case Base_X3DConstants .SFVec3f:
            case Base_X3DConstants .SFVec4f:
            case Base_X3DConstants .MFColor:
            case Base_X3DConstants .MFColorRGBA:
            case Base_X3DConstants .MFFloat:
            case Base_X3DConstants .MFMatrix3f:
            case Base_X3DConstants .MFMatrix4f:
            case Base_X3DConstants .MFVec2f:
            case Base_X3DConstants .MFVec3f:
            case Base_X3DConstants .MFVec4f:
               value .type = "float";
               break;
            case Base_X3DConstants .SFInt32:
            case Base_X3DConstants .SFImage:
            case Base_X3DConstants .MFInt32:
            case Base_X3DConstants .MFImage:
               value .type = "integer";
               break;
            case Base_X3DConstants .SFString:
            case Base_X3DConstants .MFString:
               value .type = "string";
               break;
         }
      }
      else if (!Array .isArray (value) && value .valueOf () instanceof Object)
      {
         // Support for Numbers (Vector234, Color34, ...).

         value = Array .from (value);
         value .type = "double";
      }

      metadataSet .getValue () .setMetaValue (last, value);

      this .processMetaDataCallback (path);
   },
   removeMetaData (path)
   {
      const names = path .split ("/");

      if (names .length < 2)
         return;

      function removeMetaData (metadataSet, names)
      {
         if (!metadataSet)
            return false;

         const name = names .shift ();

         if (!names .length || removeMetaData (metadataSet .getValue () .getMetadataObject ("MetadataSet", name), names))
         {
            metadataSet .getValue () .removeMetaValue (name);
         }

         return !metadataSet .value .length;
      }

      if (removeMetaData (this .getMetadataSet ([names .shift ()]), names))
         this ._metadata = null;
   },
   getMetadataSet (names, create = false)
   {
      const name = names .shift ();

      let metadataSet = this ._metadata .valueOf ();

      if (metadataSet ?.getNodeTypeName () !== "MetadataSet" || metadataSet ?.name !== name)
      {
         if (!create)
            return null;

         this ._metadata = this .getExecutionContext () .createNode ("MetadataSet");
         metadataSet     = this ._metadata .valueOf ();

         this .getExecutionContext () .addNamedNode (this .getExecutionContext () .getUniqueName (name), metadataSet);

         metadataSet .reference = this .getBrowser () .getBrowserOption ("MetadataReference");
         metadataSet .name      = name;
      }

      for (const name of names)
         metadataSet = metadataSet .getValue () .getMetadataObject ("MetadataSet", name, create);

      return metadataSet;
   },
   [_metaDataCallbacks]: new Map (),
   addMetaDataCallback (key, path, callback)
   {
      if (!this .hasOwnProperty (_metaDataCallbacks))
         this [_metaDataCallbacks] = new Map ();

      let map = this [_metaDataCallbacks] .get (path);

      if (!map)
         this [_metaDataCallbacks] .set (path, map = new Map ());

      map .set (key, callback);
   },
   removeMetaDataCallback (key, path)
   {
      const map = this [_metaDataCallbacks] .get (path);

      if (!map)
         return;

      map .delete (key);

      if (map .size === 0)
         this [_metaDataCallbacks] .delete (path);
   },
   processMetaDataCallback (path)
   {
      const map = this [_metaDataCallbacks] .get (path);

      if (!map)
         return;

      for (const callback of map .values ())
         callback ();
   },
   toStream (generator)
   {
      generator .string += this .getTypeName ();
      generator .TidySpace ();
      generator .string += "{";
      generator .TidySpace ();
      generator .string += "}";
   },
   toVRMLStream (generator)
   {
      generator .CheckSpace ();

      if (generator .outputNames)
      {
         const name = generator .Name (this);

         if (name .length)
         {
            if (generator .ExistsNode (this))
            {
               generator .string += "USE";
               generator .Space ();
               generator .string += name;
               generator .NeedsSpace ();
               return;
            }
         }

         if (name .length)
         {
            generator .AddNode (this);

            generator .string += "DEF";
            generator .Space ();
            generator .string += name;
            generator .Space ();
         }
      }

      generator .string += this .getTypeName ();
      generator .TidySpace ();
      generator .string += "{";

      const
         fields            = this .getConvertedFields (this .getChangedFields ()),
         userDefinedFields = this .getUserDefinedFields ();

      if (this .canUserDefinedFields ())
      {
         if (userDefinedFields .length)
         {
            let
               fieldTypeLength  = 0,
               accessTypeLength = 0;

            for (const field of userDefinedFields)
            {
               fieldTypeLength  = Math .max (fieldTypeLength, field .getTypeName () .length);
               accessTypeLength = Math .max (accessTypeLength, generator .AccessType (field .getAccessType ()) .length);
            }

            generator .TidyBreak ();
            generator .IncIndent ();

            const last = userDefinedFields .at (-1);

            for (const field of userDefinedFields)
            {
               this .toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength);

               if (field === last)
                  generator .TidyBreak ();
               else
                  generator .Break ();
            }

            generator .DecIndent ();

            if (fields .length !== 0)
               generator .TidyBreak ();
         }
      }

      if (fields .length === 0)
      {
         if (userDefinedFields .length)
            generator .Indent ();
         else
            generator .TidySpace ();
      }
      else
      {
         const last = fields .at (-1);

         if (userDefinedFields .length === 0)
            generator .TidyBreak ();

         generator .IncIndent ();

         for (const field of fields)
         {
            this .toVRMLStreamField (generator, field);

            if (field === last)
               generator .TidyBreak ();
            else
               generator .Break ();
         }

         generator .DecIndent ();
         generator .Indent ();
      }

      generator .string += "}";
   },
   toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength)
   {
      const sharedNode = generator .IsSharedNode (this);

      if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode)
      {
         generator .Indent ();
         generator .string += generator .AccessType (field .getAccessType ()) .padEnd (accessTypeLength, generator .TidySpace ());
         generator .Space ();
         generator .string += field .getTypeName () .padEnd (fieldTypeLength, generator .TidySpace ());
         generator .Space ();
         generator .string += field .getName ();

         if (field .isInitializable ())
         {
            generator .Space ();

            field .toVRMLStream (generator);
         }
      }
      else
      {
         let
            index                  = 0,
            initializableReferences = false;

         for (const reference of field .getReferences ())
         {
            initializableReferences ||= reference .isInitializable ();

            // Output user defined reference field

            generator .Indent ();
            generator .string += generator .AccessType (field .getAccessType ()) .padEnd (accessTypeLength, generator .TidySpace ());
            generator .Space ();
            generator .string += field .getTypeName () .padEnd (fieldTypeLength, generator .TidySpace ());
            generator .Space ();
            generator .string += field .getName ();
            generator .Space ();
            generator .string += "IS";
            generator .Space ();
            generator .string += reference .getName ();

            ++ index;

            if (index !== field .getReferences () .size)
               generator .Break ();
         }

         if (field .getAccessType () === Base_X3DConstants .inputOutput && !initializableReferences && !field .isDefaultValue ())
         {
            generator .Break ();
            generator .Indent ();
            generator .string += generator .AccessType (field .getAccessType ()) .padEnd (accessTypeLength, generator .TidySpace ());
            generator .Space ();
            generator .string += field .getTypeName () .padEnd (fieldTypeLength, generator .TidySpace ());
            generator .Space ();
            generator .string += field .getName ();

            if (field .isInitializable ())
            {
               generator .Space ();

               field .toVRMLStream (generator);
            }
         }
      }
   },
   toVRMLStreamField (generator, field)
   {
      const sharedNode = generator .IsSharedNode (this);

      if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode)
      {
         if (field .isInitializable ())
         {
            generator .Indent ();
            generator .string += field .getName ();
            generator .Space ();

            field .toVRMLStream (generator);
         }
      }
      else
      {
         let
            index                  = 0,
            initializableReferences = false;

         for (const reference of field .getReferences ())
         {
            initializableReferences ||= reference .isInitializable ();

            // Output build in reference field

            generator .Indent ();
            generator .string += field .getName ();
            generator .Space ();
            generator .string += "IS";
            generator .Space ();
            generator .string += reference .getName ();

            ++ index;

            if (index !== field .getReferences () .size)
               generator .Break ();
         }

         if (field .getAccessType () === Base_X3DConstants .inputOutput && !initializableReferences && !this .isDefaultValue (field))
         {
            // Output build in field

            generator .Break ();
            generator .Indent ();
            generator .string += field .getName ();
            generator .Space ();

            field .toVRMLStream (generator);
         }
      }
   },
   toXMLStream (generator)
   {
      const
         sharedNode = generator .IsSharedNode (this),
         name       = generator .Name (this);

      if (name .length)
      {
         if (generator .ExistsNode (this))
         {
            generator .openTag (this .getTypeName ());

            if (generator .html && this .getTypeName () === "Script")
               generator .attribute ("type", "model/x3d+xml");

            generator .attribute ("USE", name);
            generator .containerField (this .getContainerField ());
            generator .closeTag (this .getTypeName ());
            return;
         }
      }

      generator .openTag (this .getTypeName ());

      if (generator .html && this .getTypeName () === "Script")
         generator .attribute ("type", "model/x3d+xml");

      if (name .length)
      {
         generator .AddNode (this);
         generator .attribute ("DEF", name);
      }

      generator .containerField (this .getContainerField ());

      const
         fields            = this .getConvertedFields (this .getChangedFields ()),
         userDefinedFields = this .getUserDefinedFields ();

      const
         references = [ ],
         childNodes = [ ];

      let cdata = this .getSourceText ();

      if (cdata ?.length === 0)
         cdata = null;

      if (!cdata ?.some (string => string .match (/^\s*(?:data|ecmascript|javascript|vrmlscript)\:/s)))
         cdata = null;

      generator .IncIndent ();
      generator .IncIndent ();

      for (const field of fields)
      {
         // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
         // for this field.

         let mustOutputValue = false;

         if (generator .ExecutionContext ())
         {
            if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
            {
               if (!Array .from (field .getReferences ()) .some (reference => reference .isInitializable ()))
                  mustOutputValue = !this .isDefaultValue (field);
            }
         }

         // If we have no execution context we are not in a proto and must not generate IS references the same is true
         // if the node is a shared node as the node does not belong to the execution context.

         if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode || mustOutputValue)
         {
            if (mustOutputValue)
               references .push (field);

            if (field .isInitializable ())
            {
               switch (field .getType ())
               {
                  case Base_X3DConstants .SFNode:
                  case Base_X3DConstants .MFNode:
                  {
                     childNodes .push (field);
                     break;
                  }
                  default:
                  {
                     if (field === cdata)
                        break;

                     generator .AttribBreak ();
                     generator .ListIndent ();
                     generator .string += field .getName ();
                     generator .string += "='";

                     field .toXMLStream (generator, field === this .getSourceText ());

                     generator .string += "'";
                     break;
                  }
               }
            }
         }
         else
         {
            references .push (field);
         }
      }

      generator .DecIndent ();
      generator .DecIndent ();

      if ((!this .canUserDefinedFields () || !userDefinedFields .length) && (!references .length || sharedNode) && !childNodes .length && !cdata)
      {
         generator .closeTag (this .getTypeName ());
      }
      else
      {
         generator .endTag ();
         generator .IncIndent ();

         if (this .canUserDefinedFields ())
         {
            for (const field of userDefinedFields)
            {
               generator .openTag ("field");
               generator .attribute ("accessType", generator .AccessType (field .getAccessType ()));
               generator .attribute ("type",       field .getTypeName ());
               generator .attribute ("name",       field .getName ());

               // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
               // for this field.

               let mustOutputValue = false;

               if (generator .ExecutionContext ())
               {
                  if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
                  {
                     if (!Array .from (field .getReferences ()) .some (reference => reference .isInitializable ()))
                        mustOutputValue = true;
                  }
               }

               if ((field .getReferences () .size === 0 || !generator .ExecutionContext ()) || sharedNode || mustOutputValue)
               {
                  if (mustOutputValue && generator .ExecutionContext ())
                     references .push (field);

                  if (!field .isInitializable () || field .isDefaultValue ())
                  {
                     if (field .getAppInfo ())
                        generator .attribute ("appinfo", field .getAppInfo ());

                     if (field .getDocumentation ())
                        generator .attribute ("documentation", field .getDocumentation ());

                     generator .closeTag ("field");
                     generator .TidyBreak ();
                  }
                  else
                  {
                     // Output value

                     switch (field .getType ())
                     {
                        case Base_X3DConstants .SFNode:
                        case Base_X3DConstants .MFNode:
                        {
                           if (field .getAppInfo ())
                              generator .attribute ("appinfo", field .getAppInfo ());

                           if (field .getDocumentation ())
                              generator .attribute ("documentation", field .getDocumentation ());

                           generator .endTag ();
                           generator .IncIndent ();
                           generator .PushContainerField (null);

                           field .toXMLStream (generator);

                           generator .TidyBreak ();
                           generator .DecIndent ();
                           generator .closingTag ("field");
                           generator .TidyBreak ();
                           generator .PopContainerField ();
                           break;
                        }
                        default:
                        {
                           generator .Space ();
                           generator .string += "value='";

                           field .toXMLStream (generator);

                           generator .string += "'";

                           if (field .getAppInfo ())
                              generator .attribute ("appinfo", field .getAppInfo ());

                           if (field .getDocumentation ())
                              generator .attribute ("documentation", field .getDocumentation ());

                           generator .closeTag ("field");
                           generator .TidyBreak ();
                           break;
                        }
                     }
                  }
               }
               else
               {
                  if (generator .ExecutionContext ())
                     references .push (field);

                  if (field .getAppInfo ())
                     generator .attribute ("appinfo", field .getAppInfo ());

                  if (field .getDocumentation ())
                     generator .attribute ("documentation", field .getDocumentation ());

                  generator .closeTag ("field");
                  generator .TidyBreak ();
               }
            }
         }

         if (references .length && !sharedNode)
         {
            generator .openingTag ("IS");
            generator .TidyBreak ();
            generator .IncIndent ();

            for (const field of references)
            {
               const protoFields = field .getReferences ();

               for (const protoField of protoFields)
               {
                  generator .openTag ("connect");
                  generator .attribute ("nodeField",  field .getName ());
                  generator .attribute ("protoField", protoField .getName ());
                  generator .closeTag ("connect");
                  generator .TidyBreak ();
               }
            }

            generator .DecIndent ();
            generator .closingTag ("IS");
            generator .TidyBreak ();
         }

         for (const field of childNodes)
         {
            generator .PushContainerField (field);

            field .toXMLStream (generator);

            generator .TidyBreak ();
            generator .PopContainerField ();
         }

         for (const value of cdata ?? [ ])
         {
            generator .string += "<![CDATA[";
            generator .string += value;
            generator .string += "]]>";
            generator .Break ();
         }

         generator .DecIndent ();
         generator .closingTag (this .getTypeName ());
      }
   },
   toJSONStream (generator)
   {
      const
         sharedNode = generator .IsSharedNode (this),
         name       = generator .Name (this);

      // Type name

      generator .beginObject (this .getTypeName (), false, true);

      // USE name

      if (name .length)
      {
         if (generator .ExistsNode (this))
         {
            generator .stringProperty ("@USE", name, false);
            generator .endObject ();
            generator .endObject ();
            return;
         }
      }

      let comma = false;

      // DEF name

      if (name .length)
      {
         generator .AddNode (this);
         generator .stringProperty ("@DEF", name, false);

         comma = true;
      }

      // Fields

      const
         fields            = this .getConvertedFields (this .getChangedFields ()),
         userDefinedFields = this .getUserDefinedFields ();

      const references = [ ];

      let sourceText = this .getSourceText ();

      // Source text

      if (sourceText ?.length !== 1)
         sourceText = null;

      if (!sourceText ?.some (string => string .match (/^\s*(?:data|ecmascript|javascript|vrmlscript)\:/s)))
         sourceText = null;

      // Predefined fields

      if (fields .length)
      {
         const outputFields = [ ];

         for (const field of fields)
         {
            // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
            // for this field.

            let mustOutputValue = false;

            if (generator .ExecutionContext ())
            {
               if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
               {
                  if (!Array .from (field .getReferences ()) .some (reference => reference .isInitializable ()))
                     mustOutputValue = !this .isDefaultValue (field);
               }
            }

            // If we have no execution context we are not in a proto and must not generate IS references the same is true
            // if the node is a shared node as the node does not belong to the execution context.

            if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode || mustOutputValue)
            {
               if (mustOutputValue)
                  references .push (field);

               if (field !== sourceText && field .isInitializable ())
                  outputFields .push (field);
            }
            else
            {
               references .push (field);
            }
         }

         for (const field of outputFields)
         {
            switch (field .getType ())
            {
               case Base_X3DConstants .SFNode:
               case Base_X3DConstants .MFNode:
               {
                  generator .beginValue (`-${field .getName ()}`, comma || field !== outputFields [0]);

                  field .toJSONStream (generator);
                  break;
               }
               default:
               {
                  generator .beginValue (`@${field .getName ()}`, comma || field !== outputFields [0]);

                  field .toJSONStream (generator);
                  break;
               }
            }
         }

         comma ||= !! outputFields .length;
      }

      // User defined fields

      if (this .canUserDefinedFields () && userDefinedFields .length)
      {
         generator .beginArray ("field", comma);

         for (const field of userDefinedFields)
         {
            generator .beginObject ("", field !== userDefinedFields [0]);
            generator .stringProperty ("@accessType", generator .AccessType (field .getAccessType ()), false);
            generator .stringProperty ("@type",       field .getTypeName ());
            generator .stringProperty ("@name",       field .getName ());

            // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
            // for this field.

            let mustOutputValue = false;

            if (generator .ExecutionContext ())
            {
               if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
               {
                  if (!Array .from (field .getReferences ()) .some (reference => reference .isInitializable ()))
                     mustOutputValue = true;
               }
            }

            if ((field .getReferences () .size === 0 || !generator .ExecutionContext ()) || sharedNode || mustOutputValue)
            {
               if (mustOutputValue && generator .ExecutionContext ())
                  references .push (field);

               if (field .isInitializable () && !field .isDefaultValue ())
               {
                  // Output value

                  switch (field .getType ())
                  {
                     case Base_X3DConstants .SFNode:
                     {
                        generator .beginArray ("-children");

                        generator .TidyBreak ();
                        generator .Indent ();

                        field .toJSONStream (generator);

                        generator .endArray ();
                        break;
                     }
                     case Base_X3DConstants .MFNode:
                     {
                        generator .beginValue ("-children");

                        field .toJSONStream (generator);
                        break;
                     }
                     default:
                     {
                        generator .beginValue ("@value");

                        field .toJSONStream (generator);
                        break;
                     }
                  }
               }
            }
            else
            {
               if (generator .ExecutionContext ())
                  references .push (field);
            }

            if (field .getAppInfo ())
               generator .stringProperty ("@appinfo", field .getAppInfo ());

            if (field .getDocumentation ())
               generator .stringProperty ("@documentation", field .getDocumentation ());

            generator .endObject ();
         }

         generator .endArray ();

         comma = true;
      }

      // Source text

      if (sourceText)
      {
         generator .beginArray ("#sourceCode", comma);

         const lines = sourceText [0] .split ("\n");

         for (const [i, line] of lines .entries ())
         {
            if (i !== 0)
               generator .string += ',';

            generator .TidyBreak ();
            generator .ListIndent ();
            generator .string += '"';
            generator .string += generator .EncodeString (line);
            generator .string += '"';
         }

         generator .endArray ();

         comma = true;
      }

      // IS references

      if (references .length && !sharedNode)
      {
         generator .beginObject ("IS", comma);
         generator .beginArray ("connect", false);

         for (const field of references)
         {
            const protoFields = Array .from (field .getReferences ());

            for (const protoField of protoFields)
            {
               generator .beginObject ("", field !== references [0] || protoField !== protoFields [0]);
               generator .stringProperty ("@nodeField",  field .getName (), false);
               generator .stringProperty ("@protoField", protoField .getName ());
               generator .endObject ();
            }
         }

         generator .endArray ();
         generator .endObject ();
      }

      // End

      generator .endObject ();
      generator .endObject ();
   },
   dispose ()
   {
      const executionContext = this .getExecutionContext ();

      // Remove named node if any.

      if (this .getName ())
         executionContext .removeNamedNode (this .getName ())

      if (executionContext .isScene)
      {
         // Remove exported node if any.

         for (const exportedNode of Array .from (executionContext .getExportedNodes ()))
         {
            if (exportedNode .getLocalNode () === this)
               executionContext .removeExportedNode (exportedNode .getExportedName ());
         }

         // Remove imported node if any.

         const parentContext = executionContext .getExecutionContext ();

         if (parentContext)
         {
            for (const importedNode of parentContext .getImportedNodes ())
               importedNode .getExportedNode () .update ();
         }
      }

      // Remove node from entire scene graph.

      for (const firstParent of Array .from (this .getParents ()))
      {
         if (!(firstParent instanceof x_ite_Fields .SFNode))
            continue;

         for (const secondParent of Array .from (firstParent .getParents ()))
         {
            if (!(secondParent instanceof x_ite_Fields .MFNode))
               continue;

            secondParent .setValue (Array .from (secondParent) .filter (node => node ?.getValue () !== this))
         }
      }

      for (const firstParent of Array .from (this .getParents ()))
      {
         if (!(firstParent instanceof x_ite_Fields .SFNode))
            continue;

         firstParent .setValue (null);
      }

      Fields_SFNodeCache .delete (this);

      // Call super.dispose, where fields get disposed.

      Base_X3DBaseNode .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DNode .prototype))
   Object .defineProperty (X3DNode .prototype, key, { enumerable: false });

Object .defineProperties (X3DNode,
{
   getStaticProperties:
   {
      value (typeName, componentName, componentLevel, containerField, fromVersion, toVersion = "Infinity")
      {
         return {
            typeName:
            {
               value: typeName,
               enumerable: true,
            },
            componentInfo:
            {
               value: Object .freeze ({ name: componentName, level: componentLevel }),
               enumerable: true,
            },
            ... containerField ?
            {
               containerField:
               {
                  value: containerField,
                  enumerable: true,
               },
            } : { },
            ... fromVersion && toVersion ?
            {
               specificationRange:
               {
                  value: Object .freeze ({ from: fromVersion, to: toVersion }),
                  enumerable: true,
               },
            } : { },
         };
      },
   },
})

Object .defineProperties (X3DNode, X3DNode .getStaticProperties ("X3DNode", "Core", 1));

const X3DNode_default_ = X3DNode;
;

/* harmony default export */ const Core_X3DNode = (Namespace/* default */.A .add ("X3DNode", X3DNode_default_));
;// ./src/x_ite/Rendering/TraverseType.js
let TraverseType_i = 0;

const TraverseType = Object .freeze ({
   BBOX:      TraverseType_i ++,
   POINTER:   TraverseType_i ++,
   CAMERA:    TraverseType_i ++,
   PICKING:   TraverseType_i ++,
   COLLISION: TraverseType_i ++,
   DEPTH:     TraverseType_i ++,
   SHADOW:    TraverseType_i ++,
   DISPLAY:   TraverseType_i ++,
});

const TraverseType_default_ = TraverseType;
;

/* harmony default export */ const Rendering_TraverseType = (Namespace/* default */.A .add ("TraverseType", TraverseType_default_));
;// ./src/x_ite/Components/Core/X3DChildNode.js





function X3DChildNode (executionContext)
{
   if (this .getExecutionContext ())
      return;

   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DChildNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "isBoundedObject",   new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isPointingObject",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isCameraObject",    new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isPickableObject",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isCollisionObject", new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isShadowObject",    new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "isVisibleObject",   new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (X3DChildNode .prototype, Core_X3DNode .prototype),
{
   setBoundedObject (value)
   {
      if (!!value !== this ._isBoundedObject .getValue ())
         this ._isBoundedObject = value;
   },
   isBoundedObject ()
   {
      return this ._isBoundedObject .getValue ();
   },
   setPointingObject (value)
   {
      if (!!value !== this ._isPointingObject .getValue ())
         this ._isPointingObject = value;
   },
   isPointingObject ()
   {
      return this ._isPointingObject .getValue ();
   },
   setCameraObject (value)
   {
      if (!!value !== this ._isCameraObject .getValue ())
         this ._isCameraObject = value;
   },
   isCameraObject ()
   {
      return this ._isCameraObject .getValue ();
   },
   setPickableObject (value)
   {
      if (!!value !== this ._isPickableObject .getValue ())
         this ._isPickableObject = value;
   },
   isPickableObject ()
   {
      return this ._isPickableObject .getValue ();
   },
   setShadowObject (value)
   {
      if (!!value !== this ._isShadowObject .getValue ())
         this ._isShadowObject = value;
   },
   isShadowObject ()
   {
      return this ._isShadowObject .getValue ();
   },
   setCollisionObject (value)
   {
      if (!!value !== this ._isCollisionObject .getValue ())
         this ._isCollisionObject = value;
   },
   isCollisionObject ()
   {
      return this ._isCollisionObject .getValue ();
   },
   setVisibleObject (value)
   {
      if (!!value !== this ._isVisibleObject .getValue ())
         this ._isVisibleObject = value;
   },
   isVisibleObject ()
   {
      return this ._isVisibleObject .getValue ();
   },
   isVisible ()
   {
      // This function will be overloaded by X3DBoundedObject.
      return true;
   },
   connectChildNode (childNode, excludes)
   {
      if (!excludes ?.includes (Rendering_TraverseType .BBOX))
      {
         childNode ._isBoundedObject .addFieldInterest (this ._isBoundedObject);
         this .setBoundedObject (childNode .isBoundedObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .POINTER))
      {
         childNode ._isPointingObject .addFieldInterest (this ._isPointingObject);
         this .setPointingObject (childNode .isPointingObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .CAMERA))
      {
         childNode ._isCameraObject .addFieldInterest (this ._isCameraObject);
         this .setCameraObject (childNode .isCameraObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .PICKING))
      {
         childNode ._isPickableObject .addFieldInterest (this ._isPickableObject);
         this .setPickableObject (childNode .isPickableObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .COLLISION))
      {
         childNode ._isCollisionObject .addFieldInterest (this ._isCollisionObject);
         this .setCollisionObject (childNode .isCollisionObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .SHADOW))
      {
         childNode ._isShadowObject .addFieldInterest (this ._isShadowObject);
         this .setShadowObject (childNode .isShadowObject ());
      }

      if (!excludes ?.includes (Rendering_TraverseType .DISPLAY))
      {
         childNode ._isVisibleObject .addFieldInterest (this ._isVisibleObject);
         this .setVisibleObject (childNode .isVisibleObject ());
      }
   },
   disconnectChildNode (childNode)
   {
      childNode ._isBoundedObject   .removeFieldInterest (this ._isBoundedObject);
      childNode ._isPointingObject  .removeFieldInterest (this ._isPointingObject);
      childNode ._isCameraObject    .removeFieldInterest (this ._isCameraObject);
      childNode ._isPickableObject  .removeFieldInterest (this ._isPickableObject);
      childNode ._isCollisionObject .removeFieldInterest (this ._isCollisionObject);
      childNode ._isShadowObject    .removeFieldInterest (this ._isShadowObject);
      childNode ._isVisibleObject   .removeFieldInterest (this ._isVisibleObject);
   },
   getShapes (shapes, modelMatrix)
   {
      return shapes;
   },
});

Object .defineProperties (X3DChildNode, Core_X3DNode .getStaticProperties ("X3DChildNode", "Core", 1));

const X3DChildNode_default_ = X3DChildNode;
;

/* harmony default export */ const Core_X3DChildNode = (Namespace/* default */.A .add ("X3DChildNode", X3DChildNode_default_));
;// ./src/standard/Math/Algorithms/SAT.js
const
   extents1 = { min: 0, max: 0 },
   extents2 = { min: 0, max: 0 };

/**
 *  Class to represent the Separating Axis Theorem.
 */
function SAT () { }

SAT .isSeparated = function (axes, points1, points2)
{
   // https://gamedev.stackexchange.com/questions/25397/obb-vs-obb-collision-detection

   for (const axis of axes)
   {
      project (points1, axis, extents1);
      project (points2, axis, extents2);

      if (overlaps (extents1 .min, extents1 .max, extents2 .min, extents2 .max))
         continue;

      return true;
   }

   return false;
};

///  Projects @a points to @a axis and returns the minimum and maximum bounds.
function project (points, axis, extents)
{
   extents .min = Number .POSITIVE_INFINITY;
   extents .max = Number .NEGATIVE_INFINITY;

   for (const point of points)
   {
      // Just dot it to get the min and max along this axis.
      // NOTE: the axis must be normalized to get accurate projections to calculate the MTV, but if it is only needed to
      // know whether it overlaps, every axis can be used.

      const dotVal = point .dot (axis);

      if (dotVal < extents .min)
         extents .min = dotVal;

      if (dotVal > extents .max)
         extents .max = dotVal;
   }
}

///  Returns true if both ranges overlap, otherwise false.
function overlaps (min1, max1, min2, max2)
{
   return is_between (min2, min1, max1) || is_between (min1, min2, max2);
}

///  Returns true if @a value is between @a lowerBound and @a upperBound, otherwise false.
function is_between (value, lowerBound, upperBound)
{
   return lowerBound <= value && value <= upperBound;
}

const SAT_default_ = SAT;
;

/* harmony default export */ const Algorithms_SAT = (Namespace/* default */.A .add ("SAT", SAT_default_));
;// ./src/standard/Math/Geometry/Box3.js




function Box3 (size, center)
{
   this .matrix = new Numbers_Matrix4 ();

   this .set (size, center);
}

Object .assign (Box3 .prototype,
{
   copy ()
   {
      const copy = Object .create (Box3 .prototype);

      copy .matrix = this .matrix .copy ();

      return copy;
   },
   assign (box)
   {
      this .matrix .assign (box .matrix);

      return this;
   },
   equals (box)
   {
      return this .matrix .equals (box .matrix);
   },
   getMatrix ()
   {
      return this .matrix;
   },
   set (size, center)
   {
      if (size && center)
      {
         this .matrix .set (size .x / 2, 0, 0, 0,
                            0, size .y / 2, 0, 0,
                            0, 0, size .z / 2, 0,
                            center .x, center .y, center .z, 1);
      }
      else
      {
         this .matrix .assign (Numbers_Matrix4 .ZERO);
      }

      return this;
   },
   setExtents (min, max)
   {
      const
         sx = (max .x - min .x) / 2,
         sy = (max .y - min .y) / 2,
         sz = (max .z - min .z) / 2,
         cx = (max .x + min .x) / 2,
         cy = (max .y + min .y) / 2,
         cz = (max .z + min .z) / 2;

      this .matrix .set (sx, 0,  0,  0,
                         0,  sy, 0,  0,
                         0,  0,  sz, 0,
                         cx, cy, cz, 1);

      return this;
   },
   getExtents (min, max)
   {
      this .getAbsoluteExtents (min, max);

      min .add (this .center);
      max .add (this .center);
   },
   getAbsoluteExtents: (() =>
   {
      const
         r1 = new Numbers_Vector3 (),
         p1 = new Numbers_Vector3 (),
         p4 = new Numbers_Vector3 ();

      return function (min, max)
      {
         const
            m = this .matrix,
            x = m .xAxis,
            y = m .yAxis,
            z = m .zAxis;

         r1 .assign (y) .add (z);

         const r2 = z .subtract (y);

         p1 .assign (x) .add (r1),
         p4 .assign (x) .add (r2);

         const
            p2 = r1 .subtract (x),
            p3 = r2 .subtract (x);

         min .assign (p1) .min (p2, p3, p4);
         max .assign (p1) .max (p2, p3, p4);

         p1 .negate ();
         p2 .negate ();
         p3 .negate ();
         p4 .negate ();

         min .min (p1, p2, p3, p4);
         max .max (p1, p2, p3, p4);
      };
   })(),
   setPoints: (() =>
   {
      const
         min = new Numbers_Vector3 (),
         max = new Numbers_Vector3 ();

      return function  (points)
      {
            min .set (Number .POSITIVE_INFINITY),
            max .set (Number .NEGATIVE_INFINITY);

         for (const point of points)
         {
            min .min (point);
            max .max (point);
         }

         return this .setExtents (min, max);
      };
   })(),
   setArray: (() =>
   {
      const
         min = new Numbers_Vector3 (),
         max = new Numbers_Vector3 ();

      return function (array, stride = 3)
      {
         const length = array .length;

         if (!length)
            return this .set ();

         let minX = Number .POSITIVE_INFINITY;
         let minY = Number .POSITIVE_INFINITY;
         let minZ = Number .POSITIVE_INFINITY;

         let maxX = Number .NEGATIVE_INFINITY;
         let maxY = Number .NEGATIVE_INFINITY;
         let maxZ = Number .NEGATIVE_INFINITY;

         for (let i = 0; i < length; i += stride)
         {
            const
               x = array [i],
               y = array [i + 1],
               z = array [i + 2];

            if (x < minX) minX = x;
            if (y < minY) minY = y;
            if (z < minZ) minZ = z;

            if (x > maxX) maxX = x;
            if (y > maxY) maxY = y;
            if (z > maxZ) maxZ = z;
         }

         return this .setExtents (min .set (minX, minY, minZ), max .set (maxX, maxY, maxZ));
      };
   })(),
   getPoints: (() =>
   {
      const
         x  = new Numbers_Vector3 (),
         y  = new Numbers_Vector3 (),
         z  = new Numbers_Vector3 (),
         r1 = new Numbers_Vector3 ();

      return function (points)
      {
         /*
          * p6 ---------- p5
          * | \           | \
          * | p2------------ p1
          * |  |          |  |
          * |  |          |  |
          * p7 |_________ p8 |
          *  \ |           \ |
          *   \|            \|
          *    p3 ---------- p4
          */

         const m = this .matrix;

         x .assign (m .xAxis);
         y .assign (m .yAxis);
         z .assign (m .zAxis);

         r1 .assign (y) .add (z);

         const r2 = z .subtract (y);

         points [0] .assign (x)  .add (r1);
         points [1] .assign (r1) .subtract (x);
         points [2] .assign (r2) .subtract (x);
         points [3] .assign (x)  .add (r2);

         points [4] .assign (points [2]) .negate ();
         points [5] .assign (points [3]) .negate ();
         points [6] .assign (points [0]) .negate ();
         points [7] .assign (points [1]) .negate ();

         const center = this .center;

         points [0] .add (center);
         points [1] .add (center);
         points [2] .add (center);
         points [3] .add (center);

         points [4] .add (center);
         points [5] .add (center);
         points [6] .add (center);
         points [7] .add (center);

         return points;
      };
   })(),
   getAxes (axes)
   {
      const m = this .matrix;

      axes [0] .assign (m .xAxis);
      axes [1] .assign (m .yAxis);
      axes [2] .assign (m .zAxis);

      return axes;
   },
   getNormals: (() =>
   {
      const
         x = new Numbers_Vector3 (),
         y = new Numbers_Vector3 (),
         z = new Numbers_Vector3 ();

      const axes = [ Numbers_Vector3 .X_AXIS, Numbers_Vector3 .Y_AXIS, Numbers_Vector3 .Z_AXIS ];

      return function (normals)
      {
         const m = this .matrix;

         x .assign (m .xAxis);
         y .assign (m .yAxis);
         z .assign (m .zAxis);

         if (x .squaredNorm () === 0)
         {
            x .assign (y) .cross (z);

            if (x .squaredNorm () === 0)
            {
               for (const axis of axes)
               {
                  x .assign (axis) .cross (y);

                  if (x .squaredNorm () !== 0)
                     break;
               }

               if (x .squaredNorm () === 0)
               {
                  for (const axis of axes)
                  {
                     x .assign (axis) .cross (z);

                     if (x .squaredNorm () !== 0)
                        break;
                  }

                  if (x .squaredNorm () === 0)
                     x .assign (Numbers_Vector3 .X_AXIS);
               }
            }
         }

         if (y .squaredNorm () === 0)
         {
            y .assign (z) .cross (x);

            if (y .squaredNorm () === 0)
            {
               for (const axis of axes)
               {
                  y .assign (axis) .cross (z);

                  if (y .squaredNorm () !== 0)
                     break;
               }

               if (y .squaredNorm () === 0)
               {
                  for (const axis of axes)
                  {
                     y .assign (axis) .cross (x);

                     if (y .squaredNorm () !== 0)
                        break;
                  }

                  if (y .squaredNorm () === 0)
                     y .assign (Numbers_Vector3 .Y_AXIS);
               }
            }
         }

         if (z .squaredNorm () === 0)
         {
            z .assign (x) .cross (y);

            if (z .squaredNorm () === 0)
            {
               for (const axis of axes)
               {
                  z .assign (axis) .cross (x);

                  if (z .squaredNorm () !== 0)
                     break;
               }

               if (z .squaredNorm () === 0)
               {
                  for (const axis of axes)
                  {
                     z .assign (axis) .cross (y);

                     if (z .squaredNorm () !== 0)
                        break;
                  }

                  if (z .squaredNorm () === 0)
                     z .assign (Numbers_Vector3 .Z_AXIS);
               }
            }
         }

         normals [0] .assign (y) .cross (z) .normalize ();
         normals [1] .assign (z) .cross (x) .normalize ();
         normals [2] .assign (x) .cross (y) .normalize ();

         return normals;
      };
   })(),
   isEmpty ()
   {
      return this .matrix [15] === 0;
   },
   add: (() =>
   {
      const
         lhs_min = new Numbers_Vector3 (),
         lhs_max = new Numbers_Vector3 (),
         rhs_min = new Numbers_Vector3 (),
         rhs_max = new Numbers_Vector3 ();

      return function (box)
      {
         if (this .isEmpty ())
            return this .assign (box);

         if (box .isEmpty ())
            return this;

         this .getExtents (lhs_min, lhs_max);
         box  .getExtents (rhs_min, rhs_max);

         return this .setExtents (lhs_min .min (rhs_min), lhs_max .max (rhs_max));
      };
   })(),
   multLeft (matrix)
   {
      this .matrix .multLeft (matrix);
      return this;
   },
   multRight (matrix)
   {
      this .matrix .multRight (matrix);
      return this;
   },
   containsPoint: (() =>
   {
      const
         min = new Numbers_Vector3 (),
         max = new Numbers_Vector3 ();

      return function (point)
      {
         this .getExtents (min, max);

         return min .x <= point .x &&
                max .x >= point .x &&
                min .y <= point .y &&
                max .y >= point .y &&
                min .z <= point .z &&
                max .z >= point .z;
      };
   })(),
   intersectsBox: (() =>
   {
      const
         points1 = Array .from ({ length: 8 }, () => new Numbers_Vector3 ()),
         points2 = Array .from ({ length: 8 }, () => new Numbers_Vector3 ()),
         axes1   = Array .from ({ length: 3 }, () => new Numbers_Vector3 ()),
         axes2   = Array .from ({ length: 3 }, () => new Numbers_Vector3 ()),
         axes9   = Array .from ({ length: 9 }, () => new Numbers_Vector3 ()),
         normals = Array .from ({ length: 3 }, () => new Numbers_Vector3 ());

      return function (other)
      {
         // Test special cases.

         if (this .isEmpty ())
            return false;

         if (other .isEmpty ())
            return false;

         // Get points.

         this  .getPoints (points1);
         other .getPoints (points2);

         // Test the three planes spanned by the normal vectors of the faces of the first parallelepiped.

         if (Algorithms_SAT .isSeparated (this .getNormals (normals), points1, points2))
            return false;

         // Test the three planes spanned by the normal vectors of the faces of the second parallelepiped.

         if (Algorithms_SAT .isSeparated (other .getNormals (normals), points1, points2))
            return false;

         // Test the nine other planes spanned by the edges of each parallelepiped.

         this  .getAxes (axes1);
         other .getAxes (axes2);

         for (let i1 = 0; i1 < 3; ++ i1)
         {
            for (let i2 = 0; i2 < 3; ++ i2)
               axes9 [i1 * 3 + i2] .assign (axes1 [i1]) .cross (axes2 [i2]);
         }

         if (Algorithms_SAT .isSeparated (axes9, points1, points2))
            return false;

         // Both boxes intersect.

         return true;
      };
   })(),
   // import Triangle3 from "./Triangle3.js";
   // intersectsTriangle: (() =>
   // {
   //    const
   //       points1        = Array .from ({ length: 8 }, () => new Vector3 ()),
   //       axes1          = Array .from ({ length: 3 }, () => new Vector3 ()),
   //       axes9          = Array .from ({ length: 9 }, () => new Vector3 ()),
   //       normals        = Array .from ({ length: 3 }, () => new Vector3 ()),
   //       triangle       = [ ],
   //       triangleNormal = [ new Vector3 () ],
   //       triangleEdges  = Array .from ({ length: 3 }, () => new Vector3 ());

   //    return function (a, b, c)
   //    {
   //       // Test special cases.

   //       if (this .isEmpty ())
   //          return false;

   //       // Get points.

   //       this .getPoints (points1);

   //       triangle [0] = a;
   //       triangle [1] = b;
   //       triangle [2] = c;

   //       // Test the three planes spanned by the normal vectors of the faces of the first parallelepiped.

   //       if (SAT .isSeparated (this .getNormals (normals), points1, triangle))
   //          return false;

   //       // Test the normal of the triangle.

   //       Triangle3 .normal (a, b, c, triangleNormal [0]);

   //       if (SAT .isSeparated (triangleNormal, points1, triangle))
   //          return false;

   //       // Test the nine other planes spanned by the edges of each parallelepiped.

   //       this .getAxes (axes1);

   //       triangleEdges [0] .assign (a) .subtract (b);
   //       triangleEdges [1] .assign (b) .subtract (c);
   //       triangleEdges [2] .assign (c) .subtract (a);

   //       for (let i1 = 0; i1 < 3; ++ i1)
   //       {
   //          for (let i2 = 0; i2 < 3; ++ i2)
   //             axes9 [i1 * 3 + i2] .assign (axes1 [i1]) .cross (triangleEdges [i2]);
   //       }

   //       if (SAT .isSeparated (axes9, points1, triangle))
   //          return false;

   //       // Box and triangle intersect.

   //       return true;
   //    };
   // })(),
   toString ()
   {
      return `${this .size}, ${this .center}`;
   },
});

Object .assign (Box3,
{
   fromExtents (min, max)
   {
      return new Box3 () .setExtents (min, max);
   },
   fromPoints (points)
   {
      return new Box3 () .setPoints (points);
   },
   fromArray (array, stride = 3)
   {
      return new Box3 () .setArray (array, stride);
   },
});

Object .defineProperties (Box3 .prototype,
{
   size:
   {
      get: (() =>
      {
         const
            min = new Numbers_Vector3 (),
            max = new Numbers_Vector3 ();

         return function ()
         {
            this .getAbsoluteExtents (min, max);

            return max .subtract (min);
         };
      })(),
      enumerable: true,
   },
   center:
   {
      get ()
      {
         return this .matrix .origin;
      },
      enumerable: true,
   },
});

const Box3_default_ = Box3;
;

/* harmony default export */ const Geometry_Box3 = (Namespace/* default */.A .add ("Box3", Box3_default_));
;// ./src/x_ite/Browser/Grouping/X3DBBoxNode.js











function X3DBBoxNode (executionContext, boundedObject)
{
   Core_X3DChildNode .call (this, executionContext);

   // Private properties

   this .boundedObject = boundedObject;
   this .bboxShape     = this .getBrowser () .getBBoxShape ();
}

Object .assign (Object .setPrototypeOf (X3DBBoxNode .prototype, Core_X3DChildNode .prototype),
{
   getMatrix: (() =>
   {
      const
         bbox    = new Geometry_Box3 (),
         matrix  = new Numbers_Matrix4 (),
         epsilon = new Numbers_Vector3 (1e-6);

      return function ()
      {
         this .boundedObject .getBBox (bbox);

         const
            browser    = this .getBrowser (),
            max        = browser .getRenderingProperty ("ContentScale") === 1 ? Numbers_Vector3 .ZERO : epsilon,
            bboxSize   = bbox .size .max (max),
            bboxCenter = bbox .center;

         return matrix .setTransform (bboxCenter, null, bboxSize);
      };
   })(),
   getShapes (shapes, parentModelMatrix)
   {
      const modelMatrix = parentModelMatrix .copy () .multLeft (this .getMatrix ());

      return this .bboxShape .getShapes (shapes, modelMatrix);
   },
   traverse (type, renderObject)
   {
      if (type === Rendering_TraverseType .PICKING)
         return;

      const modelViewMatrix = renderObject .getModelViewMatrix ();

      modelViewMatrix .push ();
      modelViewMatrix .multLeft (this .getMatrix ());

      this .bboxShape .traverse (type, renderObject);

      modelViewMatrix .pop ();
   },
});

Object .defineProperties (X3DBBoxNode,
{
   ... Core_X3DNode .getStaticProperties ("X3DBBoxNode", "Grouping", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const X3DBBoxNode_default_ = X3DBBoxNode;
;

/* harmony default export */ const Grouping_X3DBBoxNode = (Namespace/* default */.A .add ("X3DBBoxNode", X3DBBoxNode_default_));
;// ./src/x_ite/Components/Grouping/X3DBoundedObject.js







function X3DBoundedObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DBoundedObject);

   this .addChildObjects (Base_X3DConstants .outputOnly, "hidden",           new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "display",          new x_ite_Fields .SFBool (true),
                          Base_X3DConstants .outputOnly, "transformSensors", new x_ite_Fields .SFTime ());

   // Units

   this ._bboxSize   .setUnit ("length");
   this ._bboxCenter .setUnit ("length");

   // Private properties

   this .childBBox            = new Geometry_Box3 (); // Must be unique for each X3DBoundedObject.
   this .transformSensorNodes = new Set ();
}

Object .assign (X3DBoundedObject .prototype,
{
   childBBox: new Geometry_Box3 (), // X3DExecutionContext needs this.
   initialize ()
   {
      this ._hidden     .addInterest ("set_visible_and_hidden__",      this);
      this ._visible    .addInterest ("set_visible_and_hidden__",      this);
      this ._bboxSize   .addInterest ("set_bboxSize_and_bboxCenter__", this);
      this ._bboxCenter .addInterest ("set_bboxSize_and_bboxCenter__", this);

      this .set_visible_and_hidden__ ();
   },
   isVisible ()
   {
      return this ._display .getValue ();
   },
   isHidden ()
   {
      return this ._hidden .getValue ();
   },
   setHidden (value)
   {
      if (value === this ._hidden .getValue ())
         return;

      this ._hidden = value;
   },
   isDefaultBBoxSize ()
   {
      return this ._bboxSize .getValue () .equals (Numbers_Vector3 .NEGATIVE_ONE);
   },
   isBBoxVisible ()
   {
      return this ._bboxDisplay .getValue ();
   },
   getBBox (nodes, bbox, shadows)
   {
      // Must be unique for each X3DBoundedObject.
      const childBBox = this .childBBox;

      // Add bounding boxes.

      bbox .set ();

      for (const node of nodes)
         bbox .add (node .getBBox ?.(childBBox, shadows) ?? childBBox .set ());

      return bbox;
   },
   getBBoxNode ()
   {
      return this .bboxNode ??= (() =>
      {
         const bboxNode = new Grouping_X3DBBoxNode (this .getExecutionContext (), this);

         bboxNode .setPrivate (true);
         bboxNode .setup ();

         return bboxNode;
      })();
   },
   addTransformSensor (transformSensorNode)
   {
      this .transformSensorNodes .add (transformSensorNode);

      this ._transformSensors = this .getBrowser () .getCurrentTime ();
   },
   removeTransformSensor (transformSensorNode)
   {
      this .transformSensorNodes .delete (transformSensorNode);

      this ._transformSensors = this .getBrowser () .getCurrentTime ();
   },
   getTransformSensors ()
   {
      return this .transformSensorNodes;
   },
   set_visible_and_hidden__ ()
   {
      const value = this ._visible .getValue () && !this ._hidden .getValue ();

      if (value === this ._display .getValue ())
         return;

      this ._display = value;
   },
   set_bboxSize_and_bboxCenter__ ()
   {
      this .getExecutionContext () ._bbox_changed = Date .now () / 1000;
   },
   dispose ()
   { },
});

Object .defineProperties (X3DBoundedObject, Core_X3DNode .getStaticProperties ("X3DBoundedObject", "Grouping", 1));

const X3DBoundedObject_default_ = X3DBoundedObject;
;

/* harmony default export */ const Grouping_X3DBoundedObject = (Namespace/* default */.A .add ("X3DBoundedObject", X3DBoundedObject_default_));
;// ./src/x_ite/Execution/NamedNodesArray.js



function NamedNodesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .getNodeName (), value]), Fields_SFNode);
}

Object .setPrototypeOf (NamedNodesArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (NamedNodesArray .prototype))
   Object .defineProperty (NamedNodesArray .prototype, key, { enumerable: false });

Object .defineProperties (NamedNodesArray,
{
   typeName:
   {
      value: "NamedNodesArray",
      enumerable: true,
   },
});

const NamedNodesArray_default_ = NamedNodesArray;
;

/* harmony default export */ const Execution_NamedNodesArray = (Namespace/* default */.A .add ("NamedNodesArray", NamedNodesArray_default_));
;// ./src/x_ite/Components/Core/X3DImportedNodeProxy.js
/* provided dependency */ var X3DImportedNodeProxy_$ = __webpack_require__(44).A;




const
   _importedName = Symbol (),
   _importedNode = Symbol (),
   X3DImportedNodeProxy_type         = Symbol ();

const X3DImportedNodeProxy_handler =
{
   get (target, key)
   {
      if (key in target)
         return target [key];

      const
         node     = target .getSharedNode (),
         property = node ?.[key];

      if (typeof property === "function")
         return property .bind (node);

      return property;
   },
   set (target, key, value)
   {
      if (key in target)
      {
         target [key] = value;
      }
      else
      {
         const node = target .getSharedNode ();

         if (node)
            node [key] = value;
      }

      return true;
   },
   has (target, key)
   {
      return key in (target .getSharedNode () ?? { });
   },
   ownKeys (target)
   {
      return Object .keys (target .getSharedNode () ?? { });
   },
   getOwnPropertyDescriptor (target, key)
   {
      return Object .getOwnPropertyDescriptor (target .getSharedNode () ?? { }, key);
   },
}

function X3DImportedNodeProxy (executionContext, importedName, type)
{
   Core_X3DNode .call (this, executionContext);

   const proxy = new Proxy (this, X3DImportedNodeProxy_handler);

   this [_importedName] = importedName;
   this [X3DImportedNodeProxy_type]         = type;

   this .setup ();

   return proxy;
}

Object .assign (Object .setPrototypeOf (X3DImportedNodeProxy .prototype, Core_X3DNode .prototype),
{
   initialize ()
   {
      Core_X3DNode .prototype .initialize .call (this);

      this .getExecutionContext () .importedNodes .addInterest ("update", this);

      this .update ();
   },
   getExtendedEventHandling ()
   {
      return false;
   },
   setTypeHint (value)
   {
      this [X3DImportedNodeProxy_type] ??= value;
   },
   getSharedNode ()
   {
      return X3DImportedNodeProxy_$.try (() => this [_importedNode] .getSharedNode ()) ?? null;
   },
   getImportedNode ()
   {
      return this [_importedNode];
   },
   getInnerNode ()
   {
      return this [_importedNode] .getSharedNode () .getInnerNode ();
   },
   getName ()
   {
      return this [_importedName];
   },
   setName (value)
   {
      this [_importedName] = value;

      this ._name_changed = Date .now () / 1000;
   },
   ... Object .fromEntries ([
      ["getComponentInfo",      "componentInfo"],
      ["getContainerField",     "containerField"],
      ["getSpecificationRange", "specificationRange"],
      ["getTypeName",           "typeName"],
   ]
   .map (([fn, property]) => [fn, function ()
   {
      return this .getSharedNode () ?.[fn] () ?? this .constructor [property];
   }])),
   ... Object .fromEntries ([
      "getType",
      "getFieldDefinitions",
      "getPredefinedField",
      "getPredefinedFields",
      "getUserDefinedField",
      "getUserDefinedFields",
      "getField",
      "getFields",
      "getChangedFields",
      "isDefaultValue",
      "hasRoutes",
   ]
   .map (fn => [fn, function (... args)
   {
      return this .getSharedNode () ?.[fn] (... args) ?? Core_X3DNode .prototype [fn] .call (this, ... args);
   }])),
   update ()
   {
      const importedNode = this .getExecutionContext () .getImportedNodes () .get (this [_importedName])
         ?? null;

      this [_importedNode] ?.getInlineNode () ._loadState .removeInterest ("set_loadState__", this);

      this [_importedNode] = importedNode;

      this [_importedNode] ?.getInlineNode () ._loadState .addInterest ("set_loadState__", this);

      this .set_loadState__ ();
   },
   set_loadState__ ()
   {
      if (this [_importedNode] ?.getInlineNode () .checkLoadState () === Base_X3DConstants .COMPLETE_STATE)
         this [X3DImportedNodeProxy_type] = this .getSharedNode () ?.constructor ?? this [X3DImportedNodeProxy_type];

      this ._typeName_changed = Date .now () / 1000;

      Base_X3DChildObject .prototype .addEvent .call (this);
   },
   toVRMLStream (generator)
   {
      generator .CheckSpace ();
      generator .string += "USE";
      generator .Space ();
      generator .string += this [_importedName];
      generator .NeedsSpace ();
   },
   toXMLStream (generator)
   {
      generator .openTag (this .getTypeName ());

      if (generator .html && this .getTypeName () === "Script")
         generator .attribute ("type", "model/x3d+xml");

      generator .attribute ("USE", this [_importedName]);
      generator .containerField (this .getContainerField ());
      generator .closeTag (this .getTypeName ());
   },
   toJSONStream (generator)
   {
      generator .beginObject (this .getTypeName (), false, true);
      generator .stringProperty ("@USE", this [_importedName], false);
      generator .endObject ();
      generator .endObject ();
   },
});

Object .defineProperties (X3DImportedNodeProxy .prototype,
{
   constructor:
   {
      get ()
      {
         return this [X3DImportedNodeProxy_type] ?? Core_X3DNode;
      },
   }
});

Object .defineProperties (X3DImportedNodeProxy, Core_X3DNode .getStaticProperties ("X3DImportedNodeProxy", "Core", 2, "children", "4.1"));

const X3DImportedNodeProxy_default_ = X3DImportedNodeProxy;
;

/* harmony default export */ const Core_X3DImportedNodeProxy = (Namespace/* default */.A .add ("X3DImportedNodeProxy", X3DImportedNodeProxy_default_));
;// ./src/x_ite/Execution/X3DImportedNode.js




const
   X3DImportedNode_executionContext = Symbol (),
   _inlineNode       = Symbol (),
   _exportedName     = Symbol (),
   X3DImportedNode_importedName     = Symbol (),
   _description      = Symbol (),
   _exportedNodes    = Symbol ();

function X3DImportedNode (executionContext, inlineNode, exportedName, importedName, description)
{
   Base_X3DObject .call (this);

   this [X3DImportedNode_executionContext] = executionContext;
   this [_inlineNode]       = inlineNode;
   this [_exportedName]     = exportedName;
   this [X3DImportedNode_importedName]     = importedName;
   this [_description]      = description;
   this [_exportedNodes]    = executionContext [_exportedNodes] ??= new Map ();
}

Object .assign (Object .setPrototypeOf (X3DImportedNode .prototype, Base_X3DObject .prototype),
{
   getExecutionContext ()
   {
      return this [X3DImportedNode_executionContext];
   },
   getInlineNode ()
   {
      return this [_inlineNode];
   },
   getExportedName ()
   {
      return this [_exportedName];
   },
   getExportedNode (type)
   {
      const exportedNode = this [_exportedNodes] .get (this [X3DImportedNode_importedName]);

      exportedNode ?.setTypeHint (type);

      return exportedNode ?? this .createExportedNode (type);
   },
   createExportedNode (type)
   {
      const exportedNode = new Core_X3DImportedNodeProxy (this .getExecutionContext (), this [X3DImportedNode_importedName], type);

      this [_exportedNodes] .set (this [X3DImportedNode_importedName], exportedNode);

      return exportedNode;
   },
   updateExportedNode ()
   {
      this [_exportedNodes] .get (this [X3DImportedNode_importedName]) ?.update ();
   },
   getSharedNode ()
   {
      const exportedNode = this .getInlineNode () .getInternalScene () .getExportedNodes () .get (this [_exportedName]);

      if (exportedNode)
         return exportedNode .getLocalNode ();

      throw new Error (`Exported node '${this [_exportedName]}' not found.`);
   },
   getImportedName ()
   {
      return this [X3DImportedNode_importedName];
   },
   [Symbol .for ("X_ITE.X3DImportedNode.setImportName")] (importedName)
   {
      const
         exportedNode  = this .getExportedNode (),
         exportedNodes = this [_exportedNodes];

      exportedNodes .delete (this [X3DImportedNode_importedName]);
      exportedNodes .set (importedName, exportedNode);

      this [X3DImportedNode_importedName] = importedName;

      exportedNode .setName (importedName);
   },
   getDescription ()
   {
      return this [_description];
   },
   setDescription (value)
   {
      this [_description] = String (value);
   },
   toVRMLStream (generator)
   {
      if (!generator .ExistsNode (this .getInlineNode ()))
         throw new Error ("X3DImportedNode.toVRMLStream: Inline node does not exist.");

      generator .AddRouteNode (this);

      const importedName = generator .ImportedName (this);

      generator .Indent ();
      generator .string += "IMPORT";
      generator .Space ();
      generator .string += generator .Name (this .getInlineNode ());
      generator .string += ".";
      generator .string += this .getExportedName ();

      if (importedName !== this .getExportedName ())
      {
         generator .Space ();
         generator .string += "AS";
         generator .Space ();
         generator .string += importedName;
      }

      if (this [_description])
      {
         generator .Space ();
         generator .string += "DESCRIPTION";
         generator .Space ();
         generator .string += '"';
         generator .string += this [_description];
         generator .string += '"';
      }
   },
   toXMLStream (generator)
   {
      if (!generator .ExistsNode (this .getInlineNode ()))
         throw new Error ("X3DImportedNode.toXMLStream: Inline node does not exist.");

      generator .AddRouteNode (this);

      const importedName = generator .ImportedName (this);

      generator .openTag ("IMPORT");
      generator .attribute ("inlineDEF",   generator .Name (this .getInlineNode ()));
      generator .attribute ("importedDEF", this .getExportedName ());

      if (importedName !== this .getExportedName ())
         generator .attribute ("AS", importedName);

      if (this [_description])
         generator .attribute ("DESCRIPTION", this [_description]);

      generator .closeTag ("IMPORT");
   },
   toJSONStream (generator)
   {
      if (!generator .ExistsNode (this .getInlineNode ()))
         throw new Error ("X3DImportedNode.toJSONStream: Inline node does not exist.");

      generator .TidyBreak ();
      generator .Indent ();

      generator .AddRouteNode (this);
      generator .beginObject ("IMPORT", false, true);
      generator .stringProperty ("@inlineDEF",   generator .Name (this .getInlineNode ()), false);
      generator .stringProperty ("@importedDEF", this .getExportedName ());

      const importedName = generator .ImportedName (this);

      if (importedName !== this .getExportedName ())
         generator .stringProperty ("@AS", importedName);

      if (this [_description])
         generator .stringProperty ("@DESCRIPTION", this [_description]);

      generator .endObject ();
      generator .endObject ();
   },
   dispose ()
   {
      for (const route of Array .from (this [X3DImportedNode_executionContext] .getRoutes ()))
      {
         if (route .getSourceNode () === this)
         {
            this [X3DImportedNode_executionContext] .deleteRoute (route);
            continue;
         }

         if (route .getDestinationNode () === this)
         {
            this [X3DImportedNode_executionContext] .deleteRoute (route);
            continue;
         }
      }

      Base_X3DObject .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DImportedNode .prototype))
   Object .defineProperty (X3DImportedNode .prototype, key, { enumerable: false });

Object .defineProperties (X3DImportedNode .prototype,
{
   inlineNode:
   {
      get ()
      {
         return Fields_SFNodeCache .get (this [_inlineNode]);
      },
      enumerable: true,
   },
   exportedName:
   {
      get: X3DImportedNode .prototype .getExportedName,
      enumerable: true,
   },
   exportedNode:
   {
      get ()
      {
         return Fields_SFNodeCache .get (this .getExportedNode ());
      },
      enumerable: true,
   },
   importedName:
   {
      get: X3DImportedNode .prototype .getImportedName,
      enumerable: true,
   },
   description:
   {
      get: X3DImportedNode .prototype .getDescription,
      set: X3DImportedNode .prototype .setDescription,
      enumerable: true,
   },
});

Object .defineProperties (X3DImportedNode,
{
   typeName:
   {
      value: "X3DImportedNode",
      enumerable: true,
   },
});

const X3DImportedNode_default_ = X3DImportedNode;
;

/* harmony default export */ const Execution_X3DImportedNode = (Namespace/* default */.A .add ("X3DImportedNode", X3DImportedNode_default_));
;// ./src/x_ite/Execution/ImportedNodesArray.js



function ImportedNodesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .getImportedName (), value]), Execution_X3DImportedNode);
}

Object .setPrototypeOf (ImportedNodesArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (ImportedNodesArray .prototype))
   Object .defineProperty (ImportedNodesArray .prototype, key, { enumerable: false });

Object .defineProperties (ImportedNodesArray,
{
   typeName:
   {
      value: "ImportedNodesArray",
      enumerable: true,
   },
});

const ImportedNodesArray_default_ = ImportedNodesArray;
;

/* harmony default export */ const Execution_ImportedNodesArray = (Namespace/* default */.A .add ("ImportedNodesArray", ImportedNodesArray_default_));
;// ./src/x_ite/Components/Networking/X3DUrlObject.js




const
   X3DUrlObject_cache                   = Symbol (),
   _autoRefreshStartTime    = Symbol (),
   _autoRefreshCompleteTime = Symbol (),
   _autoRefreshId           = Symbol ();

function X3DUrlObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DUrlObject);

   this .addChildObjects (Base_X3DConstants .outputOnly, "loadState", new x_ite_Fields .SFInt32 (Base_X3DConstants .NOT_STARTED_STATE),
                          Base_X3DConstants .outputOnly, "loadData",  new x_ite_Fields .SFTime ());

   this [X3DUrlObject_cache]                = true;
   this [_autoRefreshStartTime] = Date .now ();
}

Object .assign (X3DUrlObject .prototype,
{
   initialize ()
   {
      this .getLive () .addInterest ("set_live__", this);

      this ._load                 .addInterest ("set_load__",        this);
      this ._url                  .addInterest ("set_url__",         this);
      this ._loadData             .addInterest ("loadData",          this);
      this ._autoRefresh          .addInterest ("set_autoRefresh__", this);
      this ._autoRefreshTimeLimit .addInterest ("set_autoRefresh__", this);
   },
   getAllowEmptyUrl ()
   {
      return false;
   },
   setLoadState (value, notify = true)
   {
      this ._loadState = value;

      switch (value)
      {
         case Base_X3DConstants .COMPLETE_STATE:
         case Base_X3DConstants .FAILED_STATE:
         {
            this [_autoRefreshCompleteTime] = Date .now ();
            this .setAutoRefreshTimer (Math .max (this ._autoRefresh .getValue (), 0));
            break;
         }
      }

      if (!notify)
         return;

      if (value === Base_X3DConstants .IN_PROGRESS_STATE)
         this .getScene () .addLoadingObject (this);
      else
         this .getScene () .removeLoadingObject (this);
   },
   checkLoadState ()
   {
      return this ._loadState .getValue ();
   },
   getLoadState ()
   {
      return this ._loadState;
   },
   setCache (value)
   {
      this [X3DUrlObject_cache] = value;
   },
   getCache ()
   {
      return this [X3DUrlObject_cache] && this .getBrowser () .getBrowserOption ("Cache");
   },
   async requestImmediateLoad (cache = true)
   {
      switch (this .checkLoadState ())
      {
         case Base_X3DConstants .IN_PROGRESS_STATE:
         {
            await this .loading ();
            return;
         }
         case Base_X3DConstants .COMPLETE_STATE:
         {
            return;
         }
         case Base_X3DConstants .FAILED_STATE:
         {
            throw new Error (`Failed loading ${this .getTypeName ()}.`);
         }
      }

      const browser = this .getBrowser ();

      if (!browser .getBrowserOption ("LoadUrlObjects") && this .getExecutionContext () !== browser .getPrivateScene ())
         return;

      if (!this ._load .getValue ())
         throw new Error (`${this .getTypeName ()}.load is false.`);

      if (this ._url .length === 0 && !this .getAllowEmptyUrl ())
      {
         this .unloadData ();
         return;
      }

      this .setCache (cache);
      this .setLoadState (Base_X3DConstants .IN_PROGRESS_STATE);

      if (this .isInitialized ())
         // Buffer prevents double load of the scene if load and url field are set at the same time.
         this ._loadData = this .getBrowser () .getCurrentTime ();
      else
         this .loadData ();

      await this .loading ();
   },
   loading ()
   {
      return new Promise ((resolve, reject) =>
      {
         const _loading = Symbol ();

         const test = () =>
         {
            switch (this .checkLoadState ())
            {
               case Base_X3DConstants .COMPLETE_STATE:
               {
                  this ._loadState .removeFieldCallback (_loading);
                  resolve ();
                  break;
               }
               case Base_X3DConstants .FAILED_STATE:
               {
                  this ._loadState .removeFieldCallback (_loading);
                  reject (new Error (`Failed loading ${this .getTypeName ()}.`));
                  break;
               }
            }
         }

         this ._loadState .addFieldCallback (_loading, test);

         test ();
      });
   },
   loadNow ()
   {
      this .setLoadState (Base_X3DConstants .NOT_STARTED_STATE);

      return this .requestImmediateLoad ();
   },
   loadData ()
   { },
   requestUnload ()
   {
      const loadState = this .checkLoadState ();

      if (loadState === Base_X3DConstants .NOT_STARTED_STATE || loadState === Base_X3DConstants .FAILED_STATE)
         return;

      this .unloadData ();
      this .setLoadState (Base_X3DConstants .NOT_STARTED_STATE);
   },
   unloadNow ()
   {
      this .requestUnload ();
   },
   unloadData ()
   { },
   setAutoRefreshTimer (autoRefreshInterval)
   {
      clearTimeout (this [_autoRefreshId]);

      if (this ._autoRefresh .getValue () <= 0)
         return;

      const autoRefreshTimeLimit = this ._autoRefreshTimeLimit .getValue ();

      if (autoRefreshTimeLimit > 0)
      {
         if ((Date .now () - this [_autoRefreshStartTime]) / 1000 > autoRefreshTimeLimit - autoRefreshInterval)
            return;
      }

      this [_autoRefreshId] = setTimeout (this .performAutoRefresh .bind (this), autoRefreshInterval * 1000);
   },
   performAutoRefresh ()
   {
      this .setLoadState (Base_X3DConstants .NOT_STARTED_STATE);
      this .requestImmediateLoad (false) .catch (Function .prototype);
   },
   set_live__ ()
   {
      if (this .getLive () .getValue ())
         this .set_autoRefresh__ ();
      else
         clearTimeout (this [_autoRefreshId]);
   },
   set_load__ ()
   {
      if (this ._load .getValue ())
         this .requestImmediateLoad () .catch (Function .prototype);
      else
         this .requestUnload ();
   },
   set_url__ ()
   {
      if (!this ._load .getValue ())
         return;

      this .setLoadState (Base_X3DConstants .NOT_STARTED_STATE);
      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_autoRefresh__ ()
   {
      if (this .checkLoadState () !== Base_X3DConstants .COMPLETE_STATE)
         return;

      const
         elapsedTime = (Date .now () - this [_autoRefreshCompleteTime]) / 1000,
         autoRefresh = Math .max (this ._autoRefresh .getValue (), 0);

      let autoRefreshInterval = autoRefresh - elapsedTime;

      if (autoRefreshInterval < 0)
         autoRefreshInterval = Math .ceil (elapsedTime / autoRefresh) * autoRefresh - elapsedTime;

      this .setAutoRefreshTimer (autoRefreshInterval);
   },
   dispose ()
   { },
});

Object .defineProperties (X3DUrlObject, Core_X3DNode .getStaticProperties ("X3DUrlObject", "Networking", 1));

const X3DUrlObject_default_ = X3DUrlObject;
;

/* harmony default export */ const Networking_X3DUrlObject = (Namespace/* default */.A .add ("X3DUrlObject", X3DUrlObject_default_));
;// ./src/x_ite/Components/Core/X3DPrototypeInstance.js





const
   X3DPrototypeInstance_fieldDefinitions = Symbol .for ("X_ITE.X3DBaseNode.fieldDefinitions"),
   _protoNode        = Symbol (),
   _protoFields      = Symbol (),
   _body             = Symbol ();

function X3DPrototypeInstance (executionContext, protoNode)
{
   this [_protoNode]        = protoNode;
   this [X3DPrototypeInstance_fieldDefinitions] = protoNode .getFieldDefinitions ();
   this [_body]             = null;

   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DPrototypeInstance);

   this .setProtoNode (protoNode, true);
}

Object .assign (Object .setPrototypeOf (X3DPrototypeInstance .prototype, Core_X3DNode .prototype),
{
   create (executionContext)
   {
      return new X3DPrototypeInstance (executionContext, this [_protoNode]);
   },
   getTypeName ()
   {
      return this [_protoNode] .getName ();
   },
   getAppInfo ()
   {
      return this [_protoNode] .getAppInfo ();
   },
   getDocumentation ()
   {
      return this [_protoNode] .getDocumentation ();
   },
   initialize ()
   {
      Core_X3DNode .prototype .initialize .call (this);

      this .realize ();
   },
   realize ()
   {
      const
         protoNode = this [_protoNode],
         proto     = protoNode .getProtoDeclaration ();

      if (protoNode .isExternProto && protoNode .checkLoadState () !== Base_X3DConstants .COMPLETE_STATE)
         return;

      // Create execution context.

      this [_body] = new Execution_X3DExecutionContext (proto .getExecutionContext (), this);

      // Copy proto.

      this .importExternProtos  (proto .getBody () .externprotos);
      this .importProtos        (proto .getBody () .protos);
      this .copyRootNodes       (proto .getBody () .rootNodes);
      this .importImportedNodes (proto .getBody () .importedNodes);
      this .copyRoutes          (proto .getBody () .routes);

      // Propagate events.

      this [_body] .rootNodes .addInterest ("set_rootNodes__", this);

      this [_body] .setup ();

      Base_X3DChildObject .prototype .addEvent .call (this);
   },
   set_rootNodes__ ()
   {
      Base_X3DChildObject .prototype .addEvent .call (this);
   },
   construct ()
   {
      const
         protoNode = this [_protoNode],
         proto     = protoNode .getProtoDeclaration ();

      // Dispose body and assign field definitions.

      this [_body] ?.dispose ();

      // If there is a proto, the externproto is completely loaded.

      if (protoNode .isExternProto)
      {
         if (protoNode .checkLoadState () !== Base_X3DConstants .COMPLETE_STATE)
         {
            this [_body] = null;

            if (this .isInitialized ())
               Base_X3DChildObject .prototype .addEvent .call (this);

            protoNode ._updateInstances .addInterest ("construct", this);
            protoNode .requestImmediateLoad () .catch (Function .prototype);

            return;
         }

         for (const protoField of proto .getUserDefinedFields ())
         {
            try
            {
               const field = this .getField (protoField .getName ());

               // Continue if different.
               if (field .getAccessType () !== protoField .getAccessType ())
                  continue;

               // Continue if different.
               if (field .getType () !== protoField .getType ())
                  continue;

               // Continue if field is eventIn or eventOut.
               if (!field .isInitializable ())
                  continue;

               // Is set during parse, or already changed by an route event.
               if (field .getModificationTime () >= 0)
                  continue;

               // Has IS references.
               if (field .getReferences () .size)
                  continue;

               // Default value of protoField is different from field.
               if (field .equals (protoField))
                  continue;

               // Update default value of field.
               field .assign (protoField);
            }
            catch
            {
               // Definition exists in proto but does not exist in extern proto.
               this .addPredefinedField (proto .getFieldDefinitions () .get (protoField .getName ()));
            }
         }
      }

      if (this .isInitialized ())
         this .realize ();

      protoNode ._updateInstances .removeInterest ("construct", this);
      protoNode ._updateInstances .addInterest ("update", this);
   },
   update ()
   {
      // Remove old fields.

      const
         oldProtoFields = this [_protoFields],
         oldFields      = Array .from (this .getPredefinedFields ());

      for (const field of oldFields)
         this .removePredefinedField (field .getName ());

      // Add new fields.

      this [_protoFields] = Array .from (this [_protoNode] .getUserDefinedFields ());

      for (const fieldDefinition of this [_protoNode] .getFieldDefinitions ())
         this .addPredefinedField (fieldDefinition);

      // Reuse old fields, and therefor routes.

      for (const protoField of this [_protoFields])
      {
         const oldFieldName = oldProtoFields .find (field => field === protoField) ?.getName ()
            ?? protoField .getName ();

         if (!oldFieldName)
            continue;

         const
            newField = this .getPredefinedFields () .get (protoField .getName ()),
            oldField = oldFields .find (field => field .getName () === oldFieldName);

         if (!oldField)
            continue;

         if (oldField .getType () !== newField .getType ())
            continue;

         // Assign default value.
         if (oldField .isInitializable ())
         {
            if (oldField .getModificationTime () < 0)
            {
               oldField .assign (newField);
               oldField .setModificationTime (-1);
            }
         }

         oldField .addParent (this);
         oldField .setAccessType (newField .getAccessType ());
         oldField .setName (newField .getName ());

         // Replace field, i.e. reuse old field.
         this .getPredefinedFields () .update (newField .getName (), newField .getName (), oldField);

         const references = Array .from (oldField .getReferences ());

         // Remove references and routes.
         for (const field of oldField .getFieldInterests ())
            oldField .removeFieldInterest (field);

         // Readd references.
         for (const field of references)
         {
            oldField .removeReference (field);
            oldField .addReference (field);
         }

         // Reconnect input routes.
         for (const route of Array .from (oldField .getInputRoutes ()))
         {
            try
            {
               const { sourceNode, sourceField } = route;

               route .getExecutionContext () .deleteRoute (route);
               route .getExecutionContext () .addRoute (sourceNode, sourceField, this, oldField .getName ());
            }
            catch
            { }
         }

         // Reconnect output routes.
         for (const route of Array .from (oldField .getOutputRoutes ()))
         {
            try
            {
               const { destinationNode, destinationField } = route;

               route .getExecutionContext () .deleteRoute (route);
               route .getExecutionContext () .addRoute (this, oldField .getName (), destinationNode, destinationField );
            }
            catch
            { }
         }

         // Remove from old fields and dispose new field.
         oldFields .splice (oldFields .indexOf (oldField), 1);
         newField .dispose ();
      }

      // Dispose unused old fields.

      for (const oldField of oldFields)
         oldField .dispose ();

      // Construct now.

      this .construct ();
   },
   getExtendedEventHandling ()
   {
      return false;
   },
   getProtoNode ()
   {
      return this [_protoNode];
   },
   setProtoNode (protoNode, construct = false)
   {
      // Disconnect old proto node.

      const oldProtoNode = this [_protoNode];

      oldProtoNode ._name_changed .removeFieldInterest (this ._typeName_changed);
      oldProtoNode ._updateInstances .removeInterest ("construct", this);
      oldProtoNode ._updateInstances .removeInterest ("update",    this);

      // Get fields from new proto node.

      this [_protoNode]        = protoNode;
      this [X3DPrototypeInstance_fieldDefinitions] = protoNode .getFieldDefinitions ();
      this [_protoFields]      = Array .from (protoNode .getUserDefinedFields ());

      protoNode ._name_changed .addFieldInterest (this ._typeName_changed);

      construct ? this .construct () : this .update ();
   },
   getBody ()
   {
      return this [_body];
   },
   getInnerNode ()
   {
      if (this [_body])
      {
         const rootNodes = this [_body] .getRootNodes ();

         if (rootNodes .length)
         {
            const rootNode = rootNodes [0];

            if (rootNode)
               return rootNode .getValue () .getInnerNode ();
         }
      }

      throw new Error ("Root node not available.");
   },
   isDefaultValue (field)
   {
      const protoNode = this [_protoNode];

      if (protoNode .isExternProto && protoNode .checkLoadState () !== Base_X3DConstants .COMPLETE_STATE)
         return field .getModificationTime () < 0;

      return Core_X3DNode .prototype .isDefaultValue .call (this, field);
   },
   importExternProtos (externprotos1)
   {
      const externprotos2 = this [_body] .externprotos;

      for (const externproto of externprotos1)
         externprotos2 .add (externproto .getName (), externproto);
   },
   importProtos (protos1)
   {
      const protos2 = this [_body] .protos;

      for (const proto of protos1)
         protos2 .add (proto .getName (), proto);
   },
   copyRootNodes (rootNodes1)
   {
      const rootNodes2 = this [_body] .getRootNodes ();

      for (const node of rootNodes1 .getValue ())
         rootNodes2 .push (node .copy (this));
   },
   importImportedNodes (importedNodes)
   {
      for (const importedNode of importedNodes)
      {
         try
         {
            const inlineNode = this [_body] .getNamedNode (importedNode .getInlineNode () .getName ());

            this [_body] .addImportedNode (inlineNode, importedNode .getExportedName (), importedNode .getImportedName ());
         }
         catch (error)
         {
            console .error (error);
         }
      }
   },
   copyRoutes (routes)
   {
      for (const route of routes)
      {
         try
         {
            const sourceNode = route .getSourceNode () instanceof Core_X3DNode
               ? this [_body] .getLocalNode (route .getSourceNode () .getName ())
               : this [_body] .getLocalNode (route .getSourceNode () .getImportedName ());

            const destinationNode = route .getDestinationNode () instanceof Core_X3DNode
               ? this [_body] .getLocalNode (route .getDestinationNode () .getName ())
               : this [_body] .getLocalNode (route .getDestinationNode () .getImportedName ());

            this [_body] .addRoute (sourceNode, route .sourceField, destinationNode, route .destinationField);
         }
         catch (error)
         {
            console .error (error);
         }
      }
   },
   toXMLStream (generator)
   {
      const sharedNode = generator .IsSharedNode (this);

      const name = generator .Name (this);

      if (name .length)
      {
         if (generator .ExistsNode (this))
         {
            generator .openTag ("ProtoInstance");
            generator .attribute ("name", this .getTypeName ());
            generator .attribute ("USE",  name);
            generator .containerField (this .getContainerField ());
            generator .closeTag ("ProtoInstance");
            return;
         }
      }

      generator .openTag ("ProtoInstance");
      generator .attribute ("name", this .getTypeName ());

      if (name .length)
      {
         generator .AddNode (this);
         generator .attribute ("DEF", name);
      }

      generator .containerField (this .getContainerField ());

      const fields = this .getChangedFields ();

      if (fields .length === 0)
      {
         generator .closeTag ("ProtoInstance");
      }
      else
      {
         generator .endTag ();
         generator .IncIndent ();

         const references = [ ];

         for (const field of fields)
         {
            // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
            // for this field.

            let mustOutputValue = false;

            if (generator .ExecutionContext ())
            {
               if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
               {
                  if (![... field .getReferences ()] .some (reference => reference .isInitializable ()))
                     mustOutputValue = !this .isDefaultValue (field);
               }
            }

            // If we have no execution context we are not in a proto and must not generate IS references the same is true
            // if the node is a shared node as the node does not belong to the execution context.

            if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode || mustOutputValue)
            {
               if (mustOutputValue)
                  references .push (field);

               switch (field .getType ())
               {
                  case Base_X3DConstants .MFNode:
                  {
                     generator .openTag ("fieldValue");
                     generator .attribute ("name", field .getName ());

                     if (field .length === 0)
                     {
                        generator .closeTag ("fieldValue");
                        generator .TidyBreak ();
                     }
                     else
                     {
                        generator .endTag ();
                        generator .IncIndent ();
                        generator .PushContainerField (null);

                        field .toXMLStream (generator);

                        generator .TidyBreak ();
                        generator .DecIndent ();
                        generator .closingTag ("fieldValue");
                        generator .TidyBreak ();
                        generator .PopContainerField ();
                     }

                     break;
                  }
                  case Base_X3DConstants .SFNode:
                  {
                     generator .openTag ("fieldValue");
                     generator .attribute ("name", field .getName ());

                     if (field .getValue () === null)
                     {
                        generator .attribute ("value", "null");
                        generator .closeTag ("fieldValue");
                        generator .TidyBreak ();
                     }
                     else
                     {
                        generator .endTag ();
                        generator .IncIndent ();
                        generator .PushContainerField (null);

                        field .toXMLStream (generator);

                        generator .TidyBreak ();
                        generator .DecIndent ();
                        generator .closingTag ("fieldValue");
                        generator .TidyBreak ();
                     }

                     generator .PopContainerField ();
                     break;
                  }
                  default:
                  {
                     generator .openTag ("fieldValue");
                     generator .attribute ("name", field .getName ());

                     generator .Space ();
                     generator .string += "value='";

                     field .toXMLStream (generator);

                     generator .string += "'";
                     generator .closeTag ("fieldValue");
                     generator .TidyBreak ();
                     break;
                  }
               }
            }
            else
            {
               references .push (field);
            }
         }

         if (references .length && !sharedNode)
         {
            generator .openingTag ("IS");
            generator .TidyBreak ();
            generator .IncIndent ();

            for (const field of references)
            {
               const protoFields = field .getReferences ();

               for (const protoField of protoFields)
               {
                  generator .openTag ("connect");
                  generator .attribute ("nodeField",  field .getName ());
                  generator .attribute ("protoField", protoField .getName ());
                  generator .closeTag ("connect");
                  generator .TidyBreak ();
               }
            }

            generator .DecIndent ();
            generator .closingTag ("IS");
            generator .TidyBreak ();
         }

         generator .DecIndent ();
         generator .closingTag ("ProtoInstance");
      }
   },
   toJSONStream (generator)
   {
      const sharedNode = generator .IsSharedNode (this);

      // Type name

      generator .beginObject ("ProtoInstance", false, true);

      const name = generator .Name (this);

      if (name .length)
      {
         if (generator .ExistsNode (this))
         {
            generator .stringProperty ("@name", this .getTypeName (), false);
            generator .stringProperty ("@USE", name);
            generator .endObject ();
            generator .endObject ();
            return;
         }
      }

      // DEF name

      if (name .length)
      {
         generator .AddNode (this);
         generator .stringProperty ("@DEF", name, false);
      }

      // Type name

      generator .stringProperty ("@name", this .getTypeName (), name .length);

      // Fields

      const
         fields     = this .getChangedFields (),
         references = [ ];

      if (fields .length)
      {
         generator .beginArray ("fieldValue");

         const outputFields = [ ];

         for (const field of fields)
         {
            // If the field is a inputOutput and we have as reference only inputOnly or outputOnly we must output the value
            // for this field.

            let mustOutputValue = false;

            if (generator .ExecutionContext ())
            {
               if (field .getAccessType () === Base_X3DConstants .inputOutput && field .getReferences () .size !== 0)
               {
                  if (![... field .getReferences ()] .some (reference => reference .isInitializable ()))
                     mustOutputValue = !this .isDefaultValue (field);
               }
            }

            // If we have no execution context we are not in a proto and must not generate IS references the same is true
            // if the node is a shared node as the node does not belong to the execution context.

            if (field .getReferences () .size === 0 || !generator .ExecutionContext () || sharedNode || mustOutputValue)
            {
               if (mustOutputValue)
                  references .push (field);

               outputFields .push (field);
            }
            else
            {
               references .push (field);
            }
         }

         for (const field of outputFields)
         {
            switch (field .getType ())
            {
               case Base_X3DConstants .SFNode:
               {
                  generator .beginObject ("", field !== outputFields [0]);
                  generator .stringProperty ("@name", field .getName (), false);
                  generator .beginArray ("-children");

                  generator .TidyBreak ();
                  generator .Indent ();

                  field .toJSONStream (generator);

                  generator .endArray ();
                  generator .endObject ();
                  break;
               }
               case Base_X3DConstants .MFNode:
               {
                  generator .beginObject ("", field !== outputFields [0]);
                  generator .stringProperty ("@name", field .getName (), false);
                  generator .beginValue ("-children");

                  field .toJSONStream (generator);

                  generator .endObject ();
                  break;
               }
               default:
               {
                  generator .beginObject ("", field !== outputFields [0]);
                  generator .stringProperty ("@name", field .getName (), false);
                  generator .beginValue ("@value");

                  field .toJSONStream (generator);

                  generator .endObject ();
                  break;
               }
            }
         }

         generator .endArray ();

         // IS references

         if (references .length && !sharedNode)
         {
            generator .beginObject ("IS");
            generator .beginArray ("connect", false);

            for (const field of references)
            {
               const protoFields = Array .from (field .getReferences ());

               for (const protoField of protoFields)
               {
                  generator .beginObject ("", field !== references [0] || protoField !== protoFields [0]);
                  generator .stringProperty ("@nodeField",  field .getName (), false);
                  generator .stringProperty ("@protoField", protoField .getName ());
                  generator .endObject ();
               }
            }

            generator .endArray ();
            generator .endObject ();
         }
      }

      // End

      generator .endObject ();
      generator .endObject ();
   },
   dispose ()
   {
      const protoNode = this [_protoNode];

      protoNode ._name_changed .removeFieldInterest (this ._typeName_changed);
      protoNode ._updateInstances .removeInterest ("construct", this);
      protoNode ._updateInstances .removeInterest ("update",    this);

      this [_body] ?.dispose ();

      Core_X3DNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (X3DPrototypeInstance, Core_X3DNode .getStaticProperties ("X3DPrototypeInstance", "Core", 2, "children", "2.0"));

const X3DPrototypeInstance_default_ = X3DPrototypeInstance;
;

/* harmony default export */ const Core_X3DPrototypeInstance = (Namespace/* default */.A .add ("X3DPrototypeInstance", X3DPrototypeInstance_default_));
;// ./src/x_ite/Prototype/X3DProtoDeclarationNode.js






const
   X3DProtoDeclarationNode_appInfo       = Symbol (),
   X3DProtoDeclarationNode_documentation = Symbol ();

function X3DProtoDeclarationNode (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DProtoDeclarationNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "updateInstances", new x_ite_Fields .SFTime ());

   // Private properties

   this [X3DProtoDeclarationNode_appInfo]       = "";
   this [X3DProtoDeclarationNode_documentation] = "";
}

Object .assign (Object .setPrototypeOf (X3DProtoDeclarationNode .prototype, Base_X3DBaseNode .prototype),
{
   canUserDefinedFields ()
   {
      return true;
   },
   getAppInfo ()
   {
      return this [X3DProtoDeclarationNode_appInfo];
   },
   setAppInfo (value)
   {
      this [X3DProtoDeclarationNode_appInfo] = String (value);
   },
   getDocumentation ()
   {
      return this [X3DProtoDeclarationNode_documentation];
   },
   setDocumentation (value)
   {
      this [X3DProtoDeclarationNode_documentation] = String (value);
   },
   createInstance (executionContext, setup = true /* non-public argument */)
   {
      if (setup === false)
      {
         return new Core_X3DPrototypeInstance (executionContext, this);
      }
      else
      {
         const instance = new Core_X3DPrototypeInstance (executionContext, this);

         instance .setup ();

         return Fields_SFNodeCache .get (instance);
      }
   },
   newInstance ()
   {
      return this .createInstance (this .getExecutionContext ());
   },
   requestUpdateInstances ()
   {
      this ._updateInstances = Date .now () / 1000;
   },
   updateInstances ()
   {
      this ._updateInstances .processEvent ();
   },
});

for (const key of Object .keys (X3DProtoDeclarationNode .prototype))
   Object .defineProperty (X3DProtoDeclarationNode .prototype, key, { enumerable: false });

Object .defineProperties (X3DProtoDeclarationNode .prototype,
{
   name:
   {
      get: X3DProtoDeclarationNode .prototype .getName,
      enumerable: true,
   },
   fields:
   {
      get: X3DProtoDeclarationNode .prototype .getFieldDefinitions,
      enumerable: true,
   },
   appInfo:
   {
      get: X3DProtoDeclarationNode .prototype .getAppInfo,
      set: X3DProtoDeclarationNode .prototype .setAppInfo,
      enumerable: true,
   },
   documentation:
   {
      get: X3DProtoDeclarationNode .prototype .getDocumentation,
      set: X3DProtoDeclarationNode .prototype .setDocumentation,
      enumerable: true,
   },
});

Object .defineProperties (X3DProtoDeclarationNode,
{
   typeName:
   {
      value: "X3DProtoDeclarationNode",
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DProtoDeclarationNode .typeName);

const X3DProtoDeclarationNode_default_ = X3DProtoDeclarationNode;
;

/* harmony default export */ const Prototype_X3DProtoDeclarationNode = (Namespace/* default */.A .add ("X3DProtoDeclarationNode", X3DProtoDeclarationNode_default_));
;// ./src/x_ite/Prototype/X3DProtoDeclaration.js







const
   X3DProtoDeclaration_body = Symbol ();

function X3DProtoDeclaration (executionContext)
{
   Prototype_X3DProtoDeclarationNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DProtoDeclaration);

   this [X3DProtoDeclaration_body] = new Execution_X3DExecutionContext (executionContext, this);
   this [X3DProtoDeclaration_body] .setLive (false);
   this .setLive (false);
}

Object .assign (Object .setPrototypeOf (X3DProtoDeclaration .prototype, Prototype_X3DProtoDeclarationNode .prototype),
{
   initialize ()
   {
      Prototype_X3DProtoDeclarationNode .prototype .initialize .call (this);

      this [X3DProtoDeclaration_body] .setup ();
   },
   getProtoDeclaration ()
   {
      return this;
   },
   getBody ()
   {
      return this [X3DProtoDeclaration_body];
   },
   getCloneCount ()
   {
      return Prototype_X3DProtoDeclarationNode .prototype .collectCloneCount .call (this);
   },
   collectCloneCount ()
   {
      return 1;
   },
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "PROTO";
      generator .Space ();
      generator .string += this .getName ();
      generator .TidySpace ();
      generator .string += "[";

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length === 0)
      {
         generator .TidySpace ();
      }
      else
      {
         let
            fieldTypeLength  = 0,
            accessTypeLength = 0;

         for (const field of userDefinedFields)
         {
            fieldTypeLength  = Math .max (fieldTypeLength, field .getTypeName () .length);
            accessTypeLength = Math .max (accessTypeLength, generator .AccessType (field .getAccessType ()) .length);
         }

         generator .TidyBreak ();

         generator .IncIndent ();

         const last = userDefinedFields .at (-1);

         for (const field of userDefinedFields)
         {
            this .toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength);

            if (field === last)
               generator .TidyBreak ();
            else
               generator .Break ();
         }

         generator .DecIndent ();

         generator .Indent ();
      }

      generator .string += "]";
      generator .TidyBreak ();

      generator .Indent ();
      generator .string += "{";
      generator .TidyBreak ();

      generator .IncIndent ();

      this [X3DProtoDeclaration_body] .toVRMLStream (generator);

      generator .DecIndent ();

      generator .Indent ();
      generator .string += "}";
   },
   toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength)
   {
      generator .Indent ();
      generator .string += generator .AccessType (field .getAccessType ()) .padEnd (accessTypeLength, generator .TidySpace ());
      generator .Space ();
      generator .string += field .getTypeName () .padEnd (fieldTypeLength, generator .TidySpace ());
      generator .Space ();
      generator .string += field .getName ();

      if (!field .isInitializable ())
         return;

      generator .Space ();

      field .toVRMLStream (generator);
   },
   toXMLStream (generator)
   {
      generator .openTag ("ProtoDeclare");
      generator .attribute ("name", this .getName ());

      if (this .getAppInfo ())
         generator .attribute ("appinfo", this .getAppInfo ());

      if (this .getDocumentation ())
         generator .attribute ("documentation", this .getDocumentation ());

      generator .endTag ();

      // <ProtoInterface>

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length !== 0)
      {
         generator .IncIndent ();
         generator .openingTag ("ProtoInterface");
         generator .TidyBreak ();
         generator .IncIndent ();

         for (const field of userDefinedFields)
         {
            generator .openTag ("field");
            generator .attribute ("accessType", generator .AccessType (field .getAccessType ()));
            generator .attribute ("type",       field .getTypeName ());
            generator .attribute ("name",       field .getName ());

            if (field .isDefaultValue () || !field .isInitializable ())
            {
               if (field .getAppInfo ())
                  generator .attribute ("appinfo", field .getAppInfo ());

               if (field .getDocumentation ())
                  generator .attribute ("documentation", field .getDocumentation ());

               generator .closeTag ("field");
               generator .TidyBreak ();
            }
            else
            {
               switch (field .getType ())
               {
                  case Base_X3DConstants .SFNode:
                  case Base_X3DConstants .MFNode:
                  {
                     if (field .getAppInfo ())
                        generator .attribute ("appinfo", field .getAppInfo ());

                     if (field .getDocumentation ())
                        generator .attribute ("documentation", field .getDocumentation ());

                     generator .endTag ();
                     generator .IncIndent ();
                     generator .PushContainerField (null);

                     field .toXMLStream (generator);

                     generator .TidyBreak ();
                     generator .DecIndent ();
                     generator .closingTag ("field");
                     generator .TidyBreak ();
                     generator .PopContainerField ();
                     break;
                  }
                  default:
                  {
                     generator .Space ();
                     generator .string += "value='";

                     field .toXMLStream (generator);

                     generator .string += "'";

                     if (field .getAppInfo ())
                        generator .attribute ("appinfo", field .getAppInfo ());

                     if (field .getDocumentation ())
                        generator .attribute ("documentation", field .getDocumentation ());

                     generator .closeTag ("field");
                     generator .TidyBreak ();
                     break;
                  }
               }
            }
         }

         generator .DecIndent ();
         generator .closingTag ("ProtoInterface");
         generator .TidyBreak ();
         generator .DecIndent ();
      }

      // </ProtoInterface>

      // <ProtoBody>

      generator .IncIndent ();
      generator .openingTag ("ProtoBody");
      generator .TidyBreak ();
      generator .IncIndent ();

      this [X3DProtoDeclaration_body] .toXMLStream (generator);

      generator .DecIndent ();
      generator .closingTag ("ProtoBody");
      generator .TidyBreak ();
      generator .DecIndent ();

      // </ProtoBody>

      generator .closingTag ("ProtoDeclare");
   },
   toJSONStream (generator)
   {
      generator .TidyBreak ();
      generator .Indent ();

      generator .beginObject ("ProtoDeclare", false, true);
      generator .stringProperty ("@name", this .getName (), false);

      if (this .getAppInfo ())
         generator .stringProperty ("@appinfo", this .getAppInfo ());

      if (this .getDocumentation ())
         generator .stringProperty ("@documentation", this .getDocumentation ());

      // Fields

      generator .beginObject ("ProtoInterface");

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length)
      {
         generator .beginArray ("field", false);

         for (const field of userDefinedFields)
         {
            generator .beginObject ("", field !== userDefinedFields [0]);
            generator .stringProperty ("@accessType", generator .AccessType (field .getAccessType ()), false);
            generator .stringProperty ("@type",       field .getTypeName ());
            generator .stringProperty ("@name",       field .getName ());

            if (!field .isDefaultValue () && field .isInitializable ())
            {
               // Output value

               switch (field .getType ())
               {
                  case Base_X3DConstants .SFNode:
                  {
                     generator .beginArray ("-children");

                     generator .TidyBreak ();
                     generator .Indent ();

                     field .toJSONStream (generator);

                     generator .endArray ();
                     break;
                  }
                  case Base_X3DConstants .MFNode:
                  {
                     generator .beginValue ("-children", true);

                     field .toJSONStream (generator);
                     break;
                  }
                  default:
                  {
                     generator .beginValue ("@value", true);

                     field .toJSONStream (generator);
                     break;
                  }
               }
            }

            if (field .getAppInfo ())
               generator .stringProperty ("@appinfo", field .getAppInfo ());

            if (field .getDocumentation ())
               generator .stringProperty ("@documentation", field .getDocumentation ());

            generator .endObject ();
         }

         generator .endArray ();
      }

      generator .endObject ();

      // ProtoBody

      generator .beginObject ("ProtoBody");
      generator .beginArray ("-children", false);

      this [X3DProtoDeclaration_body] .toJSONStream (generator);

      generator .endArray ();
      generator .endObject ();

      // End

      generator .endObject ();
      generator .endObject ();
   },
});

for (const key of Object .keys (X3DProtoDeclaration .prototype))
   Object .defineProperty (X3DProtoDeclaration .prototype, key, { enumerable: false });

Object .defineProperties (X3DProtoDeclaration .prototype,
{
   isExternProto:
   {
      value: false,
      enumerable: true,
   },
});

Object .defineProperties (X3DProtoDeclaration,
{
   typeName:
   {
      value: "X3DProtoDeclaration",
      enumerable: true,
   },
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DProtoDeclaration .typeName);

const X3DProtoDeclaration_default_ = X3DProtoDeclaration;
;

/* harmony default export */ const Prototype_X3DProtoDeclaration = (Namespace/* default */.A .add ("X3DProtoDeclaration", X3DProtoDeclaration_default_));
;// ./src/x_ite/Parser/X3DParser.js
/* provided dependency */ var X3DParser_$ = __webpack_require__(44).A;


function X3DParser (scene, name = "Parser")
{
   this .name              = name;
   this .scene             = scene;
   this .executionContexts = [ scene ];
   this .prototypes        = [ ];
   this .placeholders      = new Map ();
   this .namedNodes        = new Map ();
   this .importedNodes     = new Map ();
   this .nodes             = new Map ();
}

Object .assign (X3DParser .prototype,
{
   getName ()
   {
      return this .name;
   },
   getBrowser ()
   {
      return this .scene .getBrowser ();
   },
   getScene ()
   {
      return this .scene;
   },
   getExecutionContext ()
   {
      return this .executionContexts .at (-1);
   },
   pushExecutionContext (executionContext)
   {
      return this .executionContexts .push (executionContext);
   },
   popExecutionContext ()
   {
      this .executionContexts .pop ();
   },
   getOuterNode ()
   {
      return this .getExecutionContext () ?.getOuterNode ();
   },
   isInsideProtoDeclaration ()
   {
      return this .getOuterNode () instanceof Prototype_X3DProtoDeclaration;
   },
   /**
    * @deprecated Directly use `browser.loadComponents`.
    */
   loadComponents ()
   {
      return this .getBrowser () .loadComponents (this .getScene ());
   },
   setUnits (units)
   {
      if (units)
         delete this .fromUnit;
      else
         this .fromUnit = function (category, value) { return value; };
   },
   fromUnit (category, value)
   {
      return this .scene .fromUnit (category, value);
   },
   convertColor (value, defaultColor = "white")
   {
      this .body ??= X3DParser_$("body");
      this .wrap ??= X3DParser_$("<div></div>") .hide ();
      this .div  ??= X3DParser_$("<div></div>") .appendTo (this .wrap);

      this .wrap .css ("color", defaultColor) .appendTo (this .body);
      this .div  .css ("color", value);

      const
         rgb    = window .getComputedStyle (this .div [0]) .color,
         values = rgb .replace (/^rgba?\(|\)$/g, "") .split (/[\s,]+/) .map (s => parseFloat (s));

      this .wrap .detach ();

      values [0] /= 255;
      values [1] /= 255;
      values [2] /= 255;

      if (typeof values [3] !== "number")
         values [3] = 1;

      return values;
   },
   sanitizeName (name = "")
   {
      // NonIdFirstChar
      name = name .replace (/^[\x30-\x39\x00-\x20\x22\x23\x27\x2b\x2c\x2d\x2e\x5b\x5c\x5d\x7b\x7d\x7f]*/, "");

      // NonIdChars
      name = name .replace (/[\x00-\x20\x22\x23\x27\x2c\x2e\x5b\x5c\x5d\x7b\x7d\x7f]+/g, "-");

      // Spaces
      name = name .trim () .replace (/[\s_-]+/g, "-");

      // Trim
      name = name .replace (/^-+|-+$/g, "");

      return name;
   },
   renameExistingNode (name)
   {
      const executionContext = this .getExecutionContext ();

      try
      {
         const namedNode = executionContext .getNamedNode (name);

         executionContext .updateNamedNode (executionContext .getUniqueName (name), namedNode);

         // console .warn (`Duplicate DEF name '${name}' in file '${executionContext .getWorldURL ()}'.`);
      }
      catch
      { }

      try
      {
         const importedName = executionContext .getUniqueImportName (name);

         executionContext .renameImportedNode (name, importedName);

         // console .warn (`Duplicate imported name '${name}' in file '${executionContext .getWorldURL ()}'.`);
      }
      catch
      { }
   },
   getNodes ()
   {
      return this .getContainer (this .nodes, Array);
   },
   getPlaceholders ()
   {
      return this .getContainer (this .placeholders, Map);
   },
   getNamedNodes ()
   {
      return this .getContainer (this .namedNodes, Map);
   },
   getImportedNodes ()
   {
      return this .getContainer (this .importedNodes, Map);
   },
   getContainer (objects, type)
   {
      const
         executionContext = this .getExecutionContext (),
         map              = objects .get (executionContext);

      if (map)
      {
         return map;
      }
      else
      {
         const map = new type ();

         objects .set (executionContext, map);

         return map;
      }
   },
   checkNodeType ()
   { },
   setupNodes ()
   {
      const
         placeholders = this .getPlaceholders (),
         nodes        = this .getNodes ();

      placeholders .forEach (placeholder => placeholder .replaceWithNode ());
      placeholders .clear ();

      if (!this .isInsideProtoDeclaration ())
         nodes .forEach (node => node .setup ());

      nodes .length = 0;;
   },
   rotateAxes90 (array)
   {
      // This function is for STL and PLY models.

      const length = array .length;

      for (let i = 0; i < length; i += 3)
      {
         const z = -array [i + 1];

         array [i + 1] = array [i + 2];
         array [i + 2] = z;
      }
   },
});

const X3DParser_default_ = X3DParser;
;

/* harmony default export */ const Parser_X3DParser = (Namespace/* default */.A .add ("X3DParser", X3DParser_default_));
;// ./src/x_ite/Parser/Expressions.js
const functions =
{
   parse (parser)
   {
      this .lastIndex = parser .lastIndex;

      parser .result = this .exec (parser .input);

      if (parser .result)
      {
         parser .lastIndex = this .lastIndex;
         return true;
      }

      return false;
   },
   lookahead (parser)
   {
      const
         lastIndex = parser .lastIndex,
         result    = this .parse (parser);

      parser .lastIndex = lastIndex;

      return result;
   },
};

function Expressions (Grammar)
{
   for (const value of Object .values (Grammar))
      Object .assign (value, functions);

   return Grammar;
}

const Expressions_default_ = Expressions;
;

/* harmony default export */ const Parser_Expressions = (Namespace/* default */.A .add ("Expressions", Expressions_default_));
;// ./src/x_ite/Parser/Placeholder.js




class Placeholder extends Core_X3DNode
{
   #parser;
   #name;
   #namedNodes;
   #importedNodes;
   #type;
   #typeName;

   constructor (parser, name, type = Core_X3DNode, typeName)
   {
      super (parser .getExecutionContext ());

      this .#parser        = parser;
      this .#namedNodes    = parser .getNamedNodes ();
      this .#importedNodes = parser .getImportedNodes ();

      this .#name     = name;
      this .#type     = type;
      this .#typeName = typeName;
   }

   getComponentInfo ()
   {
      return this .#type .componentInfo;
   }

   getContainerField ()
   {
      return this .#type .containerField;
   }

   getSpecificationRange ()
   {
      return this .#type .specificationRange;
   }

   replaceWithNode ()
   {
      const
         name      = this .#name,
         localNode = this .#namedNodes .get (name) ?? this .#importedNodes .get (name);

      const node = localNode instanceof Execution_X3DImportedNode
         ? localNode .getExportedNode (this .#type)
         : localNode;

      if (node)
      {
         this .#parser .checkNodeType (node, this .#name, this .#type, this .#typeName);

         for (const parent of Array .from (this .getParents ()))
         {
            if (!(parent instanceof x_ite_Fields .SFNode))
               continue;

            parent .setValue (node);
         }
      }
      else
      {
         console .warn (`${this .#parser .getName ()}: Named or imported node '${name}' not found.`);

         this .dispose ();
      }
   }
}

const Placeholder_default_ = Placeholder;
;

/* harmony default export */ const Parser_Placeholder = (Namespace/* default */.A .add ("Placeholder", Placeholder_default_));
;// ./src/x_ite/Parser/VRMLParser.js
/* provided dependency */ var VRMLParser_$ = __webpack_require__(44).A;




















/*
 *  Grammar
 */

// VRML lexical elements
const Grammar = Parser_Expressions ({
   // General
   Whitespaces: /[\x20\n,\t\r]+/y,
   Comment:     /#\/\*.*?\*\/#|#[^\r\n]*(?=[\r\n]|$)/sy,
   LineFeed:    /\n/g,

   // Header
   Header:      /^#(VRML|X3D) V(.*?) (utf8)\b(.*?)[\r\n]/y,

   // Keywords
   AS:          /AS/y,
   COMPONENT:   /COMPONENT/y,
   DEF:         /DEF/y,
   DESCRIPTION: /DESCRIPTION/y,
   EXPORT:      /EXPORT/y,
   EXTERNPROTO: /EXTERNPROTO/y,
   FALSE:       /FALSE|false/y,
   IMPORT:      /IMPORT/y,
   IS:          /IS/y,
   META:        /META/y,
   NULL:        /NULL|null/y,
   TRUE:        /TRUE|true/y,
   PROFILE:     /PROFILE/y,
   PROTO:       /PROTO/y,
   ROUTE:       /ROUTE/y,
   TO:          /TO/y,
   UNIT:        /UNIT/y,
   USE:         /USE/y,

   // Terminal symbols
   OpenBrace:    /\{/y,
   CloseBrace:   /\}/y,
   OpenBracket:  /\[/y,
   CloseBracket: /\]/y,
   Period:       /\./y,
   Colon:        /\:/y,

   Id: /[^\x30-\x39\x00-\x20\x22\x23\x27\x2b\x2c\x2d\x2e\x5b\x5c\x5d\x7b\x7d\x7f]{1}[^\x00-\x20\x22\x23\x27\x2c\x2e\x5b\x5c\x5d\x7b\x7d\x7f]*/y,
   ComponentNameId: /[^\x30-\x39\x00-\x20\x22\x23\x27\x2b\x2c\x2d\x2e\x5b\x5c\x5d\x7b\x7d\x7f\x3a]{1}[^\x00-\x20\x22\x23\x27\x2c\x2e\x5b\x5c\x5d\x7b\x7d\x7f\x3a]*/y,

   initializeOnly: /initializeOnly/y,
   inputOnly:      /inputOnly/y,
   outputOnly:     /outputOnly/y,
   inputOutput:    /inputOutput/y,

   field:        /field/y,
   eventIn:      /eventIn/y,
   eventOut:     /eventOut/y,
   exposedField: /exposedField/y,

   FieldType: /[SM]F(?:Bool|Color(?:RGBA)?|Double|Float|Image|Int32|Matrix[34][df]|Node|Quaternion|Rotation|String|Time|Vec[234][df])/y,

   // Values
   int32: /(?:0[xX][\da-fA-F]+)|(?:[+-]?\d+)/y,
   double: /[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+(?:\.)?))(?:[eE][+-]?\d+)?)/y,
   doubleQuotes: /"/y,
   noDoubleQuotes: /[^"]+/y,

   CONSTANTS: /([+-]?)\b(NAN|INFINITY|INF|PI(?:2|1_4|2_4|3_4|4_4|5_4|6_4|7_4|8_4|1_2|2_2|3_2|4_2|1_3|2_3|3_3|4_3|5_3|6_3)?|SQRT(?:1_2|2))\b/iy,
   HTMLColor: /0[xX][\da-fA-F]+|[a-zA-Z]+\([^\)]*\)|[a-zA-Z]+/y,
});

/*
 *  Parser
 */

function VRMLParser (scene)
{
   Parser_X3DParser .call (this, scene, "Parser");
}

Object .assign (Object .setPrototypeOf (VRMLParser .prototype, Parser_X3DParser .prototype),
{
   accessTypes: new Map ([
      ["field",          Base_X3DConstants .initializeOnly],
      ["eventIn",        Base_X3DConstants .inputOnly],
      ["eventOut",       Base_X3DConstants .outputOnly],
      ["exposedField",   Base_X3DConstants .inputOutput],
      ["initializeOnly", Base_X3DConstants .initializeOnly],
      ["inputOnly",      Base_X3DConstants .inputOnly],
      ["outputOnly",     Base_X3DConstants .outputOnly],
      ["inputOutput",    Base_X3DConstants .inputOutput],
   ]),
   SFImage: new x_ite_Fields .SFImage (),
   SFNode: new x_ite_Fields .SFNode (),
   MFString: new x_ite_Fields .MFString (),
   Color3: new Numbers_Color3 (),
   Color4: new Numbers_Color4 (),
   Matrix3: new Numbers_Matrix3 (),
   Matrix4: new Numbers_Matrix4 (),
   Quaternion: new Numbers_Quaternion (),
   Rotation4: new Numbers_Rotation4 (),
   Vector2: new Numbers_Vector2 (),
   Vector3: new Numbers_Vector3 (),
   Vector4: new Numbers_Vector4 (),
   CONSTANTS: new Map ([
      ["NAN",      Number .NaN],
      ["INFINITY", Number .POSITIVE_INFINITY],
      ["INF",      Number .POSITIVE_INFINITY],
      ["PI",    Math .PI],
      ["PI2",   Math .PI * 2],
      ["PI1_4", Math .PI * 1/4],
      ["PI2_4", Math .PI * 2/4],
      ["PI3_4", Math .PI * 3/4],
      ["PI4_4", Math .PI * 4/4],
      ["PI5_4", Math .PI * 5/4],
      ["PI6_4", Math .PI * 6/4],
      ["PI7_4", Math .PI * 7/4],
      ["PI8_4", Math .PI * 8/4],
      ["PI1_2", Math .PI * 1/2],
      ["PI2_2", Math .PI * 2/2],
      ["PI3_2", Math .PI * 3/2],
      ["PI4_2", Math .PI * 4/2],
      ["PI1_3", Math .PI * 1/3],
      ["PI2_3", Math .PI * 2/3],
      ["PI3_3", Math .PI * 3/3],
      ["PI4_3", Math .PI * 4/3],
      ["PI5_3", Math .PI * 5/3],
      ["PI6_3", Math .PI * 6/3],
      ["SQRT1_2", Math .SQRT1_2],
      ["SQRT2",   Math .SQRT2],
   ]),
   unknownLevel: 0,
   getEncoding ()
   {
      return "STRING";
   },
   setInput (vrmlSyntax)
   {
      this .input      = vrmlSyntax;
      this .lineNumber = 1;
      this .lastIndex  = 0;
   },
   isValid ()
   {
      if (typeof this .input !== "string")
         return false;

      if (this .input .match (/^#VRML V1.0/))
         return false;

      return !! this .input .match (/^(?:#X3D|#VRML|(?:[\x20\n,\t\r]|#\/\*.*?\*\/#|#[^\r\n]*[\r\n])*(?:PROFILE|COMPONENT|META|UNIT|EXTERNPROTO|PROTO|DEF|NULL|IMPORT|EXPORT|ROUTE|\w+(?:[\x20\n,\t\r]|#\/\*.*?\*\/#|#[^\r\n]*[\r\n])*\{|$))/s);
   },
   parseIntoScene (resolve, reject)
   {
      try
      {
         this .resolve = resolve;
         this .reject  = reject;

         this .getScene () .setEncoding ("VRML");
         this .getScene () .setProfile (this .getBrowser () .getProfile ("Full"));
         this .x3dScene ();
      }
      catch (error)
      {
         throw new Error (this .getError (error));
      }
   },
   getError (error)
   {
      if (DEVELOPMENT)
         console .error (error);

      const
         string     = error .message,
         unexpected = this .lastIndex === this .input .length ? "Unexpected end of file. " : "";

      let
         rest     = this .getLine (),
         line     = this .getLastLine (),
         lastLine = this .getLastLine (),
         linePos  = line .length - rest .length + 1;

      if (line .length > 80)
      {
         line     = line .substring (linePos - 40, linePos + 40);
         lastLine = "";
         linePos  = 40;
      }

      // Get world URL.

      let worldURL = this .getExecutionContext () .getWorldURL ();

      if (worldURL .startsWith ("data:"))
         worldURL = worldURL .substring (0, worldURL .indexOf (","));

      // Format error.

      const message = "\n"
         + `********************************************************************************\n`
         + `Parser error at line ${this .lineNumber}:${linePos}\n`
         + `in '${worldURL}'\n`
         + `\n`
         + `${lastLine}\n`
         + `${line}\n`
         + `${Array (linePos) .join (" ")}^\n`
         + `${unexpected}${string}\n`
         + `********************************************************************************\n`
      ;

      return message;
   },
   getLine ()
   {
      let
         input     = this .input,
         lastIndex = this .lastIndex,
         line      = "";

      while (lastIndex < input .length && input [lastIndex] !== "\n" && input [lastIndex] !== "\r")
         line += input [lastIndex ++];

      this .lastIndex = lastIndex;

      return line;
   },
   getLastLine ()
   {
      let
         input     = this .input,
         lastIndex = Math .min (this .lastIndex, this .input .length - 1),
         line      = "";

      if (lastIndex < input .length && (input [lastIndex] !== "\n" || input [lastIndex] !== "\r"))
         -- lastIndex;

      while (lastIndex >= 0 && input [lastIndex] !== "\n" && input [lastIndex] !== "\r")
         line = input [lastIndex --] + line;

      this .lastIndex = lastIndex;

      return line;
   },
   comments ()
   {
      while (this .comment ())
         ;
   },
   comment ()
   {
      if (this .whitespaces ())
         return true;

      return Grammar .Comment .parse (this);
   },
   whitespaces ()
   {
      if (Grammar .Whitespaces .parse (this))
      {
         this .lines (this .result [0]);

         return true;
      }

      return false;
   },
   lines (string)
   {
      const match = string .match (Grammar .LineFeed);

      if (match)
         this .lineNumber += match .length;
   },
   x3dScene: (() =>
   {
      const VRML =
      [
         "EnvironmentalSensor",
         "PointingDeviceSensor",
         "Scripting",
         "Sound",
         "Text",
      ];

      return function ()
      {
         const
            browser = this .getBrowser (),
            scene   = this .getScene ();

         this .headerStatement ();
         this .profileStatement ();
         this .componentStatements ();
         this .unitStatements ();
         this .metaStatements ();

         if (scene .getSpecificationVersion () === "2.0")
         {
            scene .setProfile (browser .getProfile ("Interchange"));

            for (const componentName of VRML)
               scene .updateComponent (browser .getComponent (componentName));
         }

         if (this .resolve)
         {
            browser .loadComponents (scene) .then (() =>
            {
               try
               {
                  this .statements (this .getExecutionContext () .rootNodes);
                  this .setupNodes ();

                  if (this .lastIndex < this .input .length)
                     throw new Error ("Unknown statement.");

                  this .resolve (scene);
               }
               catch (error)
               {
                  throw new Error (this .getError (error));
               }
            })
            .catch (this .reject);
         }
         else
         {
            this .statements (this .getExecutionContext () .rootNodes);
            this .setupNodes ();

            if (this .lastIndex < this .input .length)
               throw new Error ("Unknown statement.");
         }
      };
   })(),
   headerStatement ()
   {
      if (Grammar .Header .parse (this))
      {
         this .lines (this .result [0]);

         this .getScene () .setSpecificationVersion (this .result [2]);
         return true;
      }

      return false;
   },
   profileStatement ()
   {
      this .comments ();

      if (Grammar .PROFILE .parse (this))
      {
         if (this .profileNameId ())
         {
            const profile = this .getBrowser () .getProfile (this .result [0]);

            this .getScene () .setProfile (profile);
            return;
         }

         throw new Error ("Expected a profile name.");
      }
   },
   componentStatements ()
   {
      let component;

      while (component = this .componentStatement ())
         this .getScene () .updateComponent (component);
   },
   componentStatement ()
   {
      this .comments ();

      if (Grammar .COMPONENT .parse (this))
      {
         if (this .componentNameId ())
         {
            const componentNameIdCharacters = this .result [0];

            this .comments ();

            if (Grammar .Colon .parse (this))
            {
               if (this .componentSupportLevel ())
               {
                  const componentSupportLevel = this .value;

                  return this .getBrowser () .getComponent (componentNameIdCharacters, componentSupportLevel);
               }

               throw new Error ("Expected a component support level.");
            }

            throw new Error ("Expected a ':' after component name.");
         }

         throw new Error ("Expected a component name.");
      }

      return null;
   },
   componentSupportLevel ()
   {
      return this .int32 ();
   },
   unitStatements ()
   {
      while (this .unitStatement ())
         ;
   },
   unitStatement ()
   {
      this .comments ();

      if (Grammar .UNIT .parse (this))
      {
         if (this .categoryNameId ())
         {
            const categoryNameId = this .result [0];

            if (this .unitNameId ())
            {
               const unitNameId = this .result [0];

               if (this .unitConversionFactor ())
               {
                  const unitConversionFactor = this .value;

                  try
                  {
                     this .getScene () .updateUnit (categoryNameId, unitNameId, unitConversionFactor);
                     return true;
                  }
                  catch (error)
                  {
                     console .warn (`Parser error at line ${this .lineNumber}: ${error .message}`);
                     return true;
                  }
               }

               throw new Error ("Expected unit conversion factor.");
            }

            throw new Error ("Expected unit name identifier.");
         }

         throw new Error ("Expected category name identifier after UNIT statement.");
      }

      return false;
   },
   unitConversionFactor ()
   {
      return this .double ();
   },
   metaStatements ()
   {
      while (this .metaStatement ())
         ;
   },
   metaStatement ()
   {
      this .comments ();

      if (Grammar .META .parse (this))
      {
         if (this .metaKey ())
         {
            const metaKey = this .value;

            if (this .metaValue ())
            {
               const metaValue = this .value;

               this .getScene () .addMetaData (metaKey, metaValue);
               return true;
            }

            throw new Error ("Expected metadata value.");
         }

         throw new Error ("Expected metadata key.");
      }

      return false;
   },
   metaKey ()
   {
      return this .string ();
   },
   metaValue ()
   {
      return this .string ();
   },
   exportStatement ()
   {
      this .comments ();

      if (Grammar .EXPORT .parse (this))
      {
         if (this .nodeNameId ())
         {
            const
               localNodeNameId = this .result [0],
               node            = this .getScene () .getLocalNode (localNodeNameId);

            let exportedNodeNameId;

            this .comments ();

            if (Grammar .AS .parse (this))
            {
               if (this .exportedNodeNameId ())
                  exportedNodeNameId = this .result [0];
               else
                  throw new Error ("No name given after AS.");
            }
            else
            {
               exportedNodeNameId = localNodeNameId;
            }

            let description = "";

            this .comments ();

            if (Grammar .DESCRIPTION .parse (this))
            {
               if (this .string ())
                  description = this .value;
            }

            try
            {
               const existingNode = this .getScene () .getExportedNode (exportedNodeNameId);

               this .getScene () .addExportedNode (this .getScene () .getUniqueExportName (exportedNodeNameId), existingNode);
            }
            catch
            { }

            this .getScene () .updateExportedNode (exportedNodeNameId, node, description);
            return true;
         }

         throw new Error ("No name given after EXPORT.");
      }

      return false;
   },
   importStatement ()
   {
      this .comments ();

      if (Grammar .IMPORT .parse (this))
      {
         if (this .nodeNameId ())
         {
            const
               inlineNodeNameId = this .result [0],
               namedNode        = this .getExecutionContext () .getNamedNode (inlineNodeNameId);

            this .comments ();

            if (Grammar .Period .parse (this))
            {
               if (this .exportedNodeNameId ())
               {
                  const exportedNodeNameId = this .result [0];

                  this .comments ();

                  let nodeNameId;

                  if (Grammar .AS .parse (this))
                  {
                     if (this .nodeNameId ())
                        nodeNameId = this .result [0];

                     else
                        throw new Error ("No name given after AS.");
                  }
                  else
                  {
                     nodeNameId = exportedNodeNameId;
                  }

                  let description = "";

                  this .comments ();

                  if (Grammar .DESCRIPTION .parse (this))
                  {
                     if (this .string ())
                        description = this .value;
                  }

                  // Rename existing imported node.

                  this .renameExistingNode (nodeNameId);

                  // Add new imported node.

                  this .getExecutionContext () .addImportedNode (namedNode, exportedNodeNameId, nodeNameId, description);

                  if (!this .getImportedNodes () .has (nodeNameId))
                  {
                     this .getImportedNodes () .set (nodeNameId, this .getExecutionContext () .getImportedNodes () .get (nodeNameId));
                  }

                  return true;
               }

               throw new Error ("Expected exported node name.");
            }

            throw new Error ("Expected a '.' after exported node name.");
         }

         throw new Error ("No name given after IMPORT statement.");
      }
      return false;
   },
   statements (field)
   {
      while (this .statement (field))
         ;
   },
   statement (field)
   {
      if (this .protoStatement ())
         return true;

      if (this .routeStatement ())
         return true;

      if (this .importStatement ())
         return true;

      if (this .exportStatement ())
         return true;

      const node = this .nodeStatement ();

      if (node !== false)
      {
         field .push (node);
         return true;
      }

      return false;
   },
   nodeStatement ()
   {
      this .comments ();

      if (Grammar .DEF .parse (this))
      {
         if (this .nodeNameId ())
            return this .node (this .result [0]);

         throw new Error ("No name given after DEF.");
      }

      if (Grammar .USE .parse (this))
      {
         if (this .nodeNameId ())
         {
            const nodeNameId = this .result [0];

            try
            {
               const localNode = this .getExecutionContext () .getLocalNode (nodeNameId);

               return localNode instanceof Execution_X3DImportedNode
                  ? localNode .getExportedNode ()
                  : localNode .getValue ();
            }
            catch
            {
               const placeholder = this .getPlaceholders () .get (nodeNameId);

               if (placeholder)
               {
                  return placeholder;
               }
               else
               {
                  const placeholder = new Parser_Placeholder (this, nodeNameId);

                  this .getPlaceholders () .set (nodeNameId, placeholder);

                  return placeholder;
               }
            }
         }

         throw new Error ("No name given after USE.");
      }

      if (Grammar .NULL .parse (this))
         return null;

      return this .node ("");
   },
   protoStatement ()
   {
      if (this .proto ())
         return true;

      if (this .externproto ())
         return true;

      return false;
   },
   protoStatements ()
   {
      while (this .protoStatement ())
         ;
   },
   proto ()
   {
      this .comments ();

      if (Grammar .PROTO .parse (this))
      {
         if (this .nodeTypeId ())
         {
            const nodeTypeId = this .result [0];

            this .comments ();

            if (Grammar .OpenBracket .parse (this))
            {
               const interfaceDeclarations = this .interfaceDeclarations ();

               this .comments ();

               if (Grammar .CloseBracket .parse (this))
               {
                  this .comments ();

                  if (Grammar .OpenBrace .parse (this))
                  {
                     const proto = new Prototype_X3DProtoDeclaration (this .getExecutionContext ());

                     for (const field of interfaceDeclarations)
                        proto .addUserDefinedField (field .getAccessType (), field .getName (), field);

                     this .pushExecutionContext (proto .getBody ());
                     this .protoBody (proto .getBody () .rootNodes);
                     this .setupNodes ();
                     this .popExecutionContext ();

                     this .comments ();

                     if (Grammar .CloseBrace .parse (this))
                     {
                        proto .setup ();

                        try
                        {
                           const existingProto = this .getExecutionContext () .getProtoDeclaration (nodeTypeId);

                           console .warn (`A proto named '${nodeTypeId}' is already defined and will be overridden.`);

                           this .getExecutionContext () .updateProtoDeclaration (this .getExecutionContext () .getUniqueProtoName (nodeTypeId), existingProto);
                        }
                        catch
                        { }

                        this .getExecutionContext () .updateProtoDeclaration (nodeTypeId, proto);

                        return true;
                     }

                     throw new Error ("Expected a '}' at the end of PROTO body.");
                  }

                  throw new Error ("Expected a '{' at the beginning of PROTO body.");
               }

               throw new Error ("Expected a ']' at the end of PROTO interface declaration.");
            }

            throw new Error ("Expected a '[' at the beginning of PROTO interface declaration.");
         }

         throw new Error ("Invalid PROTO definition name.");
      }

      return false;
   },
   protoBody (rootNodes)
   {
      this .protoStatements ();

      const rootNodeStatement = this .rootNodeStatement ();

      if (rootNodeStatement !== false)
         rootNodes .push (rootNodeStatement);

      this .statements (rootNodes);
   },
   rootNodeStatement ()
   {
      this .comments ();

      if (Grammar .DEF .parse (this))
      {
         if (this .nodeNameId ())
         {
            const
               nodeNameId = this .result [0],
               baseNode   = this .node (nodeNameId);

            if (baseNode !== false)
               return baseNode;

            throw new Error ("Expected node type name after DEF.");
         }

         throw new Error ("No name given after DEF.");
      }

      const baseNode = this .node ("");

      if (baseNode !== false)
         return baseNode;

      return false;
   },
   interfaceDeclarations ()
   {
      const interfaceDeclarations = [ ];

      let field;

      while (field = this .interfaceDeclaration ())
         interfaceDeclarations .push (field);

      return interfaceDeclarations;
   },
   restrictedInterfaceDeclaration ()
   {
      this .comments ();

      if (Grammar .inputOnly .parse (this) || Grammar .eventIn .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .inputOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .inputOnly);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ();

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      if (Grammar .outputOnly .parse (this) || Grammar .eventOut .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .outputOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .outputOnly);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ();

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      if (Grammar .initializeOnly .parse (this) || Grammar .field .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .initializeOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               if (this .fieldValue (field))
               {
                  field .setAccessType (Base_X3DConstants .initializeOnly);
                  field .setName (fieldId);

                  return field;
               }

               throw new Error (`Couldn't read value for field '${fieldId}'.`);
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ();

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      return null;
   },
   interfaceDeclaration ()
   {
      const field = this .restrictedInterfaceDeclaration ();

      if (field)
         return field;

      this .comments ();

      if (Grammar .inputOutput .parse (this) || Grammar .exposedField .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .inputOutputId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               if (this .fieldValue (field))
               {
                  field .setAccessType (Base_X3DConstants .inputOutput);
                  field .setName (fieldId);

                  return field;
               }

               throw new Error (`Couldn't read value for field '${fieldId}'.`);
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ();

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      return null;
   },
   externproto ()
   {
      this .comments ();

      if (Grammar .EXTERNPROTO .parse (this))
      {
         if (this .nodeTypeId ())
         {
            const nodeTypeId = this .result [0];

            this .comments ();

            if (Grammar .OpenBracket .parse (this))
            {
               const externInterfaceDeclarations = this .externInterfaceDeclarations ();

               this .comments ();

               if (Grammar .CloseBracket .parse (this))
               {
                  if (this .URLList (this .MFString))
                  {
                     const externproto = new Prototype_X3DExternProtoDeclaration (this .getExecutionContext (), this .MFString);

                     for (const field of externInterfaceDeclarations)
                        externproto .addUserDefinedField (field .getAccessType (), field .getName (), field);

                     externproto .setup ();

                     try
                     {
                        const existingExternProto = this .getExecutionContext () .getExternProtoDeclaration (nodeTypeId);

                        console .warn (`A extern proto named '${nodeTypeId}' is already defined and will be overridden.`);

                        this .getExecutionContext () .updateExternProtoDeclaration (this .getExecutionContext () .getUniqueExternProtoName (nodeTypeId), existingExternProto);
                     }
                     catch
                     { }

                     this .getExecutionContext () .updateExternProtoDeclaration (nodeTypeId, externproto);
                     return true;
                  }

                  throw new Error (`Expected a URL list after EXTERNPROTO interface declaration '${nodeTypeId}'.`);
               }

               throw new Error ("Expected a ']' at the end of EXTERNPROTO interface declaration.");
            }

            throw new Error ("Expected a '[' at the beginning of EXTERNPROTO interface declaration.");
         }

         throw new Error ("Invalid EXTERNPROTO definition name.");
      }

      return false;
   },
   externInterfaceDeclarations ()
   {
      const externInterfaceDeclarations = [ ];

      let field;

      while (field = this .externInterfaceDeclaration ())
         externInterfaceDeclarations .push (field);

      return externInterfaceDeclarations;
   },
   externInterfaceDeclaration ()
   {
      this .comments ();

      if (Grammar .inputOnly .parse (this) || Grammar .eventIn .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .inputOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .inputOnly);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ()

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      if (Grammar .outputOnly .parse (this) || Grammar .eventOut .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .outputOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .outputOnly);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ()

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      if (Grammar .initializeOnly .parse (this) || Grammar .field .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .initializeOnlyId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .initializeOnly);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ()

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      if (Grammar .inputOutput .parse (this) || Grammar .exposedField .parse (this))
      {
         if (this .fieldType ())
         {
            const fieldType = this .result [0];

            if (this .inputOutputId ())
            {
               const
                  fieldId = this .result [0],
                  field   = new (x_ite_Fields [fieldType]) ();

               field .setAccessType (Base_X3DConstants .inputOutput);
               field .setName (fieldId);

               return field;
            }

            throw new Error ("Expected a name for field.");
         }

         this .Id ()

         throw new Error (`Unknown event or field type: '${this .result [0]}'.`);
      }

      return null;
   },
   URLList (field)
   {
      return this .mfstringValue (field);
   },
   routeStatement ()
   {
      this .comments ();

      if (Grammar .ROUTE .parse (this))
      {
         if (this .nodeNameId ())
         {
            const
               fromNodeId = this .result [0],
               fromNode   = this .getExecutionContext () .getLocalNode (fromNodeId);

            this .comments ();

            if (Grammar .Period .parse (this))
            {
               if (this .outputOnlyId ())
               {
                  const eventOutId = this .result [0];

                  this .comments ();

                  if (Grammar .TO .parse (this))
                  {
                     if (this .nodeNameId ())
                     {
                        const
                           toNodeId = this .result [0],
                           toNode   = this .getExecutionContext () .getLocalNode (toNodeId);

                        this .comments ();

                        if (Grammar .Period .parse (this))
                        {
                           if (this .inputOnlyId ())
                           {
                              try
                              {
                                 const eventInId = this .result [0];

                                 this .getExecutionContext () .addRoute (fromNode, eventOutId, toNode, eventInId);
                                 return true;
                              }
                              catch (error)
                              {
                                 console .warn (`Parser error at line ${this .lineNumber}: ${error .message}`);

                                 return true;
                              }
                           }

                           throw new Error ("Bad ROUTE specification: Expected a field name.");
                        }

                        throw new Error ("Bad ROUTE specification: Expected a '.' after node name.");
                     }

                     throw new Error ("Bad ROUTE specification: Expected a node name.");
                  }

                  throw new Error ("Bad ROUTE specification: Expected a 'TO'.");
               }

               throw new Error ("Bad ROUTE specification: Expected a field name.");
            }

            throw new Error ("Bad ROUTE specification: Expected a '.' after node name.");
         }

         throw new Error ("Bad ROUTE specification: Expected a node name.");
      }

      return false;
   },
   node (nodeNameId)
   {
      if (this .nodeTypeId ())
      {
         const nodeTypeId = this .result [0];

         const baseNode = this .getExecutionContext () .createNode (nodeTypeId, false)
            ?? this .getExecutionContext () .createProto (nodeTypeId, false);

         if (!baseNode)
         {
            // Parse unknown node.

            if (!this .unknownLevel)
            {
               console .warn (`Parser error at line ${this .lineNumber}: Unknown node type or proto '${nodeTypeId}'. You probably have insufficient component/profile statements, and/or an inappropriate specification version.`);
            }

            this .comments ();

            if (Grammar .OpenBrace .parse (this))
            {
               ++ this .unknownLevel;

               this .nodeBody (this .getExecutionContext () .createNode ("WorldInfo", false));

               -- this .unknownLevel;

               this .comments ();

               if (Grammar .CloseBrace .parse (this))
                  return null;
            }

            return false;
         }

         if (nodeNameId .length)
         {
            if (!this .getNamedNodes () .has (nodeNameId))
               this .getNamedNodes () .set (nodeNameId, baseNode);

            this .renameExistingNode (nodeNameId);

            this .getExecutionContext () .updateNamedNode (nodeNameId, baseNode);
         }

         this .comments ();

         if (Grammar .OpenBrace .parse (this))
         {
            if (baseNode .canUserDefinedFields ())
               this .scriptBody (baseNode);

            else
               this .nodeBody (baseNode);

            this .comments ();

            if (Grammar .CloseBrace .parse (this))
            {
               if (!this .isInsideProtoDeclaration ())
                  this .getNodes () .push (baseNode);

               return baseNode;
            }

            throw new Error ("Expected '}' at the end of node body.");
         }

         throw new Error ("Expected '{' at the beginning of node body.");
      }

      return false;
   },
   scriptBody (baseNode)
   {
      while (this .scriptBodyElement (baseNode))
         ;
   },
   scriptBodyElement (baseNode)
   {
      const { lastIndex, lineNumber } = this;

      if (this .Id ())
      {
         const accessType = this .accessTypes .get (this .result [0]);

         if (accessType)
         {
            if (this .fieldType ())
            {
               const fieldType = this .result [0];

               if (this .Id ())
               {
                  const fieldId = this .result [0];

                  this .comments ();

                  if (Grammar .IS .parse (this))
                  {
                     if (this .isInsideProtoDeclaration ())
                     {
                        if (this .Id ())
                        {
                           const isId = this .result [0];

                           let reference;

                           try
                           {
                              reference = this .getOuterNode () .getField (isId);
                           }
                           catch
                           {
                              console .warn (`Parser error at line ${this .lineNumber}: No such event or field '${isId}' inside PROTO ${this .getOuterNode () .getName ()} interface declaration.`);

                              return true;
                           }

                           if (Base_X3DConstants [fieldType] === reference .getType ())
                           {
                              if (reference .isReference (accessType))
                              {
                                 let field = VRMLParser_$.try (() => baseNode .getUserDefinedField (fieldId));

                                 if (!field)
                                 {
                                    field = new (x_ite_Fields [fieldType]) ();

                                    baseNode .addUserDefinedField (accessType, fieldId, field);
                                 }

                                 if (accessType === field .getAccessType () && reference .getType () === field .getType ())
                                 {
                                    field .addReference (reference);
                                    return true;
                                 }

                                 throw new Error (`Couldn't add field '${fieldId}', field already exists with different access type or data type.`);
                              }

                              throw new Error (`Field '${fieldId}' and '${reference .getName ()}' in PROTO '${this .getOuterNode () .getName ()}' are incompatible as an IS mapping.`);
                           }

                           throw new Error (`Field '${fieldId}' and '${reference .getName ()}' in PROTO '${this .getOuterNode () .getName ()}' have different types.`);
                        }

                        throw new Error ("No name give after IS statement.");
                     }

                     throw new Error ("IS statement outside PROTO definition.");
                  }
               }
            }
         }
      }

      this .lastIndex  = lastIndex;
      this .lineNumber = lineNumber;

      const field = this .interfaceDeclaration ();

      if (field)
      {
         const existingField = VRMLParser_$.try (() => baseNode .getUserDefinedField (field .getName ()));

         if (existingField)
         {
            if (existingField .getAccessType () === field .getAccessType () && existingField .getType () === field .getType ())
            {
               existingField .assign (field);
               return true;
            }

            throw new Error (`Couldn't set value for field '${field .getName ()}', field already exists with different access type or data type.`);
         }

         baseNode .addUserDefinedField (field .getAccessType (), field .getName (), field);
         return true;
      }

      return this .nodeBodyElement (baseNode);
   },
   nodeBody (baseNode)
   {
      while (this .nodeBodyElement (baseNode))
         ;
   },
   nodeBodyElement (baseNode)
   {
      if (this .protoStatement ())
         return true;

      if (this .routeStatement ())
         return true;

      if (this .Id ())
      {
         const fieldId = this .result [0];

         let field;

         try
         {
            field = baseNode .getPredefinedField (fieldId);
         }
         catch
         {
            // Parse unknown field value.

            const { lastIndex, lineNumber } = this;

            if (this .unknownValue ())
            {
               if (!this .unknownLevel)
               {
                  console .warn (`Parser error at line ${lineNumber}: Unknown field '${fieldId}' in class '${baseNode .getTypeName ()}'.`);
               }

               return true;
            }

            this .lastIndex  = lastIndex;
            this .lineNumber = lineNumber;

            throw new Error (`Unknown field '${fieldId}' in class '${baseNode .getTypeName ()}'.`);
         }

         this .comments ();

         if (Grammar .IS .parse (this))
         {
            if (this .isInsideProtoDeclaration ())
            {
               if (this .Id ())
               {
                  const isId = this .result [0];

                  let reference;

                  try
                  {
                     reference = this .getOuterNode () .getField (isId);
                  }
                  catch
                  {
                     console .warn (`Parser error at line ${this .lineNumber}: No such event or field '${isId}' inside PROTO ${this .getOuterNode () .getName ()}`);

                     return true;
                  }

                  if (field .getType () === reference .getType ())
                  {
                     if (reference .isReference (field .getAccessType ()))
                     {
                        field .addReference (reference);
                        return true;
                     }

                     throw new Error (`Field '${field .getName ()}' and '${reference .getName ()}' in PROTO ${this .getOuterNode () .getName ()} are incompatible as an IS mapping.`);
                  }

                  throw new Error (`Field '${field .getName ()}' and '${reference .getName ()}' in PROTO ${this .getOuterNode () .getName ()} have different types.`);
               }

               throw new Error ("No name give after IS statement.");
            }

            throw new Error ("IS statement outside PROTO definition.");
         }

         if (field .isInitializable ())
         {
            if (this .fieldValue (field))
               return true;

            throw new Error (`Couldn't read value for field '${fieldId}'.`);
         }

         // Parse value of a inputOnly or outputOnly, and output a warning.

         if (!this .unknownValue ())
            throw new Error (`Couldn't read value for field '${fieldId}'.`);

         console .warn (`Parser error at line ${this .lineNumber}: Couldn't assign value to ${this .accessTypeToString (field .getAccessType ())} field '${fieldId}'.`);

         return true;
      }

      return false;
   },
   profileNameId () { return this .Id (); },
   componentNameId ()
   {
      this .comments ();

      return Grammar .ComponentNameId .parse (this);
   },
   categoryNameId () { return this .Id (); },
   unitNameId () { return this .Id (); },
   exportedNodeNameId () { return this .Id (); },
   nodeNameId () { return this .Id (); },
   nodeTypeId () { return this .Id (); },
   initializeOnlyId () { return this .Id (); },
   inputOnlyId () { return this .Id (); },
   outputOnlyId () { return this .Id (); },
   inputOutputId () { return this .Id (); },
   Id ()
   {
      this .comments ();

      return Grammar .Id .parse (this);
   },
   fieldType ()
   {
      this .comments ();

      return Grammar .FieldType .parse (this);
   },
   fieldValue (field)
   {
      return this [field .getType ()] (field, field .getUnit ());
   },
   bool ()
   {
      this .comments ();

      if (Grammar .TRUE .parse (this))
      {
         this .value = true;
         return true;
      }

      if (Grammar .FALSE .parse (this))
      {
         this .value = false;
         return true;
      }

      return false;
   },
   double ()
   {
      this .comments ();

      if (Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);
         return true;
      }

      // Constants

      if (Grammar .CONSTANTS .parse (this))
      {
         this .value = this .CONSTANTS .get (this .result [2] .toUpperCase ());

         if (this .result [1] === "-")
            this .value = -this .value;

         return true;
      }

      return false;
   },
   int32 ()
   {
      this .comments ();

      if (Grammar .int32 .parse (this))
      {
         this .value = parseInt (this .result [0]);
         return true;
      }

      return false;
   },
   string ()
   {
      this .comments ();

      if (Grammar .doubleQuotes .parse (this))
      {
         let value = "";

         while (Grammar .noDoubleQuotes .parse (this))
         {
            value += this .result [0];

            const m = value .match (/\\+$/);

            if (!m || m [0] .length % 2 === 0)
               break;

            Grammar .doubleQuotes .parse (this);

            value += '"';
         }

         if (Grammar .doubleQuotes .parse (this))
         {
            this .lines (value);

            this .value = x_ite_Fields .SFString .unescape (value);

            return true;
         }
      }

      return false;
   },
   sfboolValue (field)
   {
      if (this .bool ())
      {
         field .setValue (this .value);
         return true;
      }

      return false;
   },
   mfboolValue (field)
   {
      field .length = 0;

      if (this .bool ())
      {
         field .push (this .value);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfboolValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfboolValues (field)
   {
      const array = [ ];

      while (this .bool ())
         array .push (this .value);

      field .setValue (array);

      return field .length !== 0;
   },
   sfcolorValue (field)
   {
      const lastIndex = this .lastIndex;

      if (this .double ())
      {
         const r = this .value;

         if (this .double ())
         {
            const g = this .value;

            if (this .double ())
            {
               const b = this .value;

               field .r = r;
               field .g = g;
               field .b = b;

               return true;
            }
         }
      }

      this .lastIndex = lastIndex;

      this .comments ();

      if (Grammar .HTMLColor .parse (this))
      {
         const color = this .convertColor (this .result [0] .replace (/0x/i, "#"));

         field .r = color [0];
         field .g = color [1];
         field .b = color [2];

         return true;
      }

      return false;
   },
   mfcolorValue (field)
   {
      field .length = 0;

      if (this .sfcolorValue (this .Color3))
      {
         field .push (this .Color3);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfcolorValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfcolorValues (field)
   {
      const array = [ ];

      while (this .double ())
         array .push (this .value);

      field .setValue (array);

      return field .length !== 0;
   },
   sfcolorrgbaValue (field)
   {
      const lastIndex = this .lastIndex;

      if (this .double ())
      {
         const r = this .value;

         if (this .double ())
         {
            const g = this .value;

            if (this .double ())
            {
               const b = this .value;

               if (this .double ())
               {
                  const a = this .value;

                  field .r = r;
                  field .g = g;
                  field .b = b;
                  field .a = a;

                  return true;
               }
            }
         }
      }

      this .lastIndex = lastIndex;

      this .comments ();

      if (Grammar .HTMLColor .parse (this))
      {
         const color = this .convertColor (this .result [0] .replace (/0x/i, "#"));

         field .r = color [0];
         field .g = color [1];
         field .b = color [2];
         field .a = color [3];

         return true;
      }

      return false;
   },
   mfcolorrgbaValue (field)
   {
      field .length = 0;

      if (this .sfcolorrgbaValue (this .Color4))
      {
         field .push (this .Color4);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfcolorValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfdoubleValue (field)
   {
      if (this .double ())
      {
         field .setValue (this .fromUnit (field .getUnit (), this .value));
         return true;
      }

      return false;
   },
   mfdoubleValue (field)
   {
      field .length = 0;

      if (this .double ())
      {
         field .push (this .fromUnit (field .getUnit (), this .value));
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfdoubleValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfdoubleValues (field)
   {
      const
         unit  = field .getUnit (),
         array = [ ];

      while (this .double ())
         array .push (this .fromUnit (unit, this .value));

      field .setValue (array);

      return field .length !== 0;
   },
   sfimageValue (field)
   {
      if (this .int32 ())
      {
         const width = this .value;

         if (this .int32 ())
         {
            const height = this .value;

            if (this .int32 ())
            {
               const
                  comp  = this .value,
                  size  = width * height,
                  array = [ ];

               for (let i = 0; i < size; ++ i)
               {
                  if (this .int32 ())
                  {
                     array .push (this .value);
                     continue;
                  }

                  return false;
               }

               field .width  = width;
               field .height = height;
               field .comp   = comp;
               field .array  = array;

               return true;
            }
         }
      }

      return false;
   },
   mfimageValue (field)
   {
      field .length = 0;

      if (this .sfimageValue (this .SFImage))
      {
         field .push (this .SFImage);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfimageValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfimageValues (field)
   {
      field .length = 0;

      const
         target = field .getTarget (),
         value  = this .SFImage;

      while (this .sfimageValue (value))
         target .push (value);

      return field .length !== 0;
   },
   sfint32Value (field)
   {
      if (this .int32 ())
      {
         field .setValue (this .value);
         return true;
      }

      return false;
   },
   mfint32Value (field)
   {
      field .length = 0;

      if (this .int32 ())
      {
         field .push (this .value);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfint32Values (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfint32Values (field)
   {
      const array = [ ];

      while (this .int32 ())
         array .push (this .value);

      field .setValue (array);

      return field .length !== 0;
   },
   sfmatrix3Value (field)
   {
      if (this .double ())
      {
         const m00 = this .value;

         if (this .double ())
         {
            const m01 = this .value;

            if (this .double ())
            {
               const m02 = this .value;

                  if (this .double ())
                  {
                     const m10 = this .value;

                     if (this .double ())
                     {
                        const m11 = this .value;

                        if (this .double ())
                        {
                           const m12 = this .value;

                           if (this .double ())
                           {
                              const m20 = this .value;

                              if (this .double ())
                              {
                                 const m21 = this .value;

                                 if (this .double ())
                                 {
                                    const m22 = this .value;

                                    field [0] = m00;
                                    field [1] = m01;
                                    field [2] = m02;
                                    field [3] = m10;
                                    field [4] = m11;
                                    field [5] = m12;
                                    field [6] = m20;
                                    field [7] = m21;
                                    field [8] = m22;

                                    return true;
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }

      return false;
   },
   mfmatrix3Value (field)
   {
      field .length = 0;

      if (this .sfmatrix3Value (this .Matrix3))
      {
         field .push (this .Matrix3);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfmatrixValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfmatrixValues (field)
   {
      const array = [ ];

      while (this .double ())
         array .push (this .value);

      field .setValue (array);

      return field .length !== 0;
   },
   sfmatrix4Value (field)
   {
      if (this .double ())
      {
         const m00 = this .value;

         if (this .double ())
         {
            const m01 = this .value;

            if (this .double ())
            {
               const m02 = this .value;

               if (this .double ())
               {
                  const m03 = this .value;

                  if (this .double ())
                  {
                     const m10 = this .value;

                     if (this .double ())
                     {
                        const m11 = this .value;

                        if (this .double ())
                        {
                           const m12 = this .value;

                           if (this .double ())
                           {
                              const m13 = this .value;

                              if (this .double ())
                              {
                                 const m20 = this .value;

                                 if (this .double ())
                                 {
                                    const m21 = this .value;

                                    if (this .double ())
                                    {
                                       const m22 = this .value;

                                       if (this .double ())
                                       {
                                          const m23 = this .value;

                                          if (this .double ())
                                          {
                                             const m30 = this .value;

                                             if (this .double ())
                                             {
                                                const m31 = this .value;

                                                if (this .double ())
                                                {
                                                   const m32 = this .value;

                                                   if (this .double ())
                                                   {
                                                      const m33 = this .value;

                                                      field [ 0] = m00;
                                                      field [ 1] = m01;
                                                      field [ 2] = m02;
                                                      field [ 3] = m03;
                                                      field [ 4] = m10;
                                                      field [ 5] = m11;
                                                      field [ 6] = m12;
                                                      field [ 7] = m13;
                                                      field [ 8] = m20;
                                                      field [ 9] = m21;
                                                      field [10] = m22;
                                                      field [11] = m23;
                                                      field [12] = m30;
                                                      field [13] = m31;
                                                      field [14] = m32;
                                                      field [15] = m33;

                                                      return true;
                                                   }
                                                }
                                             }
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }

      return false;
   },
   mfmatrix4Value (field)
   {
      field .length = 0;

      if (this .sfmatrix4Value (this .Matrix4))
      {
         field .push (this .Matrix4);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfmatrixValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfnodeValue (field)
   {
      const baseNode = this .nodeStatement ();

      if (baseNode !== false)
      {
         field .setValue (baseNode);
         return true;
      }

      return false;
   },
   mfnodeValue (field)
   {
      field .length = 0;

      const node = this .nodeStatement ();

      if (node !== false)
      {
         field .push (node);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .nodeStatements (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   nodeStatements (field)
   {
      this .statements (field);

      // let node = this .nodeStatement ();

      // while (node !== false)
      // {
      //    field .push (node);

      //    node = this .nodeStatement ();
      // }
   },
   sfquaternionValue (field)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const z = this .value;

               if (this .double ())
               {
                  const w = this .value;

                  field .x = x;
                  field .y = y;
                  field .z = z;
                  field .w = w;

                  return true;
               }
            }
         }
      }

      return false;
   },
   mfquaternionValue (field)
   {
      field .length = 0;

      if (this .sfquaternionValue (this .Quaternion))
      {
         field .push (this .Quaternion);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfquaternionValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfquaternionValues (field)
   {
      field .length = 0;

      const
         target = field .getTarget (),
         value  = this .Quaternion;

      while (this .sfquaternionValue (value))
         target .push (value);

      return field .length !== 0;
   },
   sfrotationValue (field)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const z = this .value;

               if (this .double ())
               {
                  const angle = this .value;

                  field .x     = x;
                  field .y     = y;
                  field .z     = z;
                  field .angle = this .fromUnit ("angle", angle);

                  return true;
               }
            }
         }
      }

      return false;
   },
   mfrotationValue (field)
   {
      field .length = 0;

      if (this .sfrotationValue (this .Rotation4))
      {
         field .push (this .Rotation4);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfrotationValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfrotationValues (field)
   {
      field .length = 0;

      const
         target = field .getTarget (),
         value  = this .Rotation4;

      while (this .sfrotationValue (value))
         target .push (value);

      return field .length !== 0;
   },
   sfstringValue (field)
   {
      if (this .string ())
      {
         field .setValue (this .value);
         return true;
      }

      return false;
   },
   mfstringValue (field)
   {
      field .length = 0;

      if (this .string ())
      {
         field .push (this .value);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfstringValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfstringValues (field)
   {
      const array = [ ];

      while (this .string ())
         array .push (this .value);

      field .setValue (array);

      return field .length !== 0;
   },
   sfvec2Value (field, unit)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            field .x = this .fromUnit (unit, x);
            field .y = this .fromUnit (unit, y);

            return true;
         }
      }

      return false;
   },
   mfvec2Value (field)
   {
      field .length = 0;

      if (this .sfvec2Value (this .Vector2, field .getUnit ()))
      {
         field .push (this .Vector2);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfvecValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfvecValues (field)
   {
      const
         unit  = field .getUnit (),
         array = [ ];

      while (this .double ())
         array .push (this .fromUnit (unit, this .value));

      field .setValue (array);

      return field .length !== 0;
   },
   sfvec3Value (field, unit)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const z = this .value;

               field .x = this .fromUnit (unit, x);
               field .y = this .fromUnit (unit, y);
               field .z = this .fromUnit (unit, z);

               return true;
            }
         }
      }

      return false;
   },
   mfvec3Value (field)
   {
      field .length = 0;

      if (this .sfvec3Value (this .Vector3, field .getUnit ()))
      {
         field .push (this .Vector3);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfvecValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfvec4Value (field, unit)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const z = this .value;

               if (this .double ())
               {
                  const w = this .value;

                  field .x = this .fromUnit (unit, x);
                  field .y = this .fromUnit (unit, y);
                  field .z = this .fromUnit (unit, z);
                  field .w = this .fromUnit (unit, w);

                  return true;
               }
            }
         }
      }

      return false;
   },
   mfvec4Value (field)
   {
      field .length = 0;

      if (this .sfvec4Value (this .Vector4, field .getUnit ()))
      {
         field .push (this .Vector4);
         return true;
      }

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfvecValues (field);

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   unknownValue ()
   {
      try
      {
         ++ this .unknownLevel;

         this .comments ();

         if (Grammar .IS .parse (this))
         {
            if (this .isInsideProtoDeclaration ())
            {
               if (this .Id ())
                  return true;
            }
         }

         if (this .mfunknownValue ())
            return true;

         return false;
      }
      finally
      {
         -- this .unknownLevel;
      }
   },
   sfunknownValue ()
   {
      if (this .sfboolValue (new x_ite_Fields .SFBool ()))
         return true;

      if (this .sfdoubleValues (new x_ite_Fields .MFFloat ()))
         return true;

      if (this .sfstringValue (new x_ite_Fields .SFString ()))
         return true;

      if (this .sfnodeValue (new x_ite_Fields .SFNode ()))
         return true;

      return false;
   },
   mfunknownValue ()
   {
      if (this .sfunknownValue ())
         return true;

      if (Grammar .OpenBracket .parse (this))
      {
         this .sfunknownValues ();

         if (Grammar .CloseBracket .parse (this))
            return true;

         throw new Error ("Expected ']'.");
      }

      return false;
   },
   sfunknownValues ()
   {
      while (this .sfunknownValue ())
         ;
   },
   accessTypeToString (accessType)
   {
      switch (accessType)
      {
         case Base_X3DConstants .initializeOnly:
            return "initializeOnly";
         case Base_X3DConstants .inputOnly:
            return "inputOnly";
         case Base_X3DConstants .outputOnly:
            return "outputOnly";
         case Base_X3DConstants .inputOutput:
            return "inputOutput";
      }
   },
});

Object .assign (VRMLParser .prototype,
{
   [Base_X3DConstants .SFBool]:       VRMLParser .prototype .sfboolValue,
   [Base_X3DConstants .SFColor]:      VRMLParser .prototype .sfcolorValue,
   [Base_X3DConstants .SFColorRGBA]:  VRMLParser .prototype .sfcolorrgbaValue,
   [Base_X3DConstants .SFDouble]:     VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFFloat]:      VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFImage]:      VRMLParser .prototype .sfimageValue,
   [Base_X3DConstants .SFInt32]:      VRMLParser .prototype .sfint32Value,
   [Base_X3DConstants .SFMatrix3f]:   VRMLParser .prototype .sfmatrix3Value,
   [Base_X3DConstants .SFMatrix3d]:   VRMLParser .prototype .sfmatrix3Value,
   [Base_X3DConstants .SFMatrix4f]:   VRMLParser .prototype .sfmatrix4Value,
   [Base_X3DConstants .SFMatrix4d]:   VRMLParser .prototype .sfmatrix4Value,
   [Base_X3DConstants .SFNode]:       VRMLParser .prototype .sfnodeValue,
   [Base_X3DConstants .SFQuaternion]: VRMLParser .prototype .sfquaternionValue,
   [Base_X3DConstants .SFRotation]:   VRMLParser .prototype .sfrotationValue,
   [Base_X3DConstants .SFString]:     VRMLParser .prototype .sfstringValue,
   [Base_X3DConstants .SFTime]:       VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFVec2d]:      VRMLParser .prototype .sfvec2Value,
   [Base_X3DConstants .SFVec2f]:      VRMLParser .prototype .sfvec2Value,
   [Base_X3DConstants .SFVec3d]:      VRMLParser .prototype .sfvec3Value,
   [Base_X3DConstants .SFVec3f]:      VRMLParser .prototype .sfvec3Value,
   [Base_X3DConstants .SFVec4d]:      VRMLParser .prototype .sfvec4Value,
   [Base_X3DConstants .SFVec4f]:      VRMLParser .prototype .sfvec4Value,

   [Base_X3DConstants .VrmlMatrix]:   VRMLParser .prototype .sfmatrix4Value,

   [Base_X3DConstants .MFBool]:       VRMLParser .prototype .mfboolValue,
   [Base_X3DConstants .MFColor]:      VRMLParser .prototype .mfcolorValue,
   [Base_X3DConstants .MFColorRGBA]:  VRMLParser .prototype .mfcolorrgbaValue,
   [Base_X3DConstants .MFDouble]:     VRMLParser .prototype .mfdoubleValue,
   [Base_X3DConstants .MFFloat]:      VRMLParser .prototype .mfdoubleValue,
   [Base_X3DConstants .MFImage]:      VRMLParser .prototype .mfimageValue,
   [Base_X3DConstants .MFInt32]:      VRMLParser .prototype .mfint32Value,
   [Base_X3DConstants .MFMatrix3d]:   VRMLParser .prototype .mfmatrix3Value,
   [Base_X3DConstants .MFMatrix3f]:   VRMLParser .prototype .mfmatrix3Value,
   [Base_X3DConstants .MFMatrix4d]:   VRMLParser .prototype .mfmatrix4Value,
   [Base_X3DConstants .MFMatrix4f]:   VRMLParser .prototype .mfmatrix4Value,
   [Base_X3DConstants .MFNode]:       VRMLParser .prototype .mfnodeValue,
   [Base_X3DConstants .MFQuaternion]: VRMLParser .prototype .mfquaternionValue,
   [Base_X3DConstants .MFRotation]:   VRMLParser .prototype .mfrotationValue,
   [Base_X3DConstants .MFString]:     VRMLParser .prototype .mfstringValue,
   [Base_X3DConstants .MFTime]:       VRMLParser .prototype .mfdoubleValue,
   [Base_X3DConstants .MFVec2d]:      VRMLParser .prototype .mfvec2Value,
   [Base_X3DConstants .MFVec2f]:      VRMLParser .prototype .mfvec2Value,
   [Base_X3DConstants .MFVec3d]:      VRMLParser .prototype .mfvec3Value,
   [Base_X3DConstants .MFVec3f]:      VRMLParser .prototype .mfvec3Value,
   [Base_X3DConstants .MFVec4d]:      VRMLParser .prototype .mfvec4Value,
   [Base_X3DConstants .MFVec4f]:      VRMLParser .prototype .mfvec4Value,
});

Base_X3DField .prototype .fromVRMLString = function (value, scene)
{
   const parser = new VRMLParser (scene);

   parser .setUnits (!!scene);
   parser .setInput (value);

   if (!parser .fieldValue (this))
      throw new Error (`Couldn't read value for field '${this .getName ()}'.`);

   parser .setupNodes ();
};

const VRMLParser_default_ = VRMLParser;
;

/* harmony default export */ const Parser_VRMLParser = (Namespace/* default */.A .add ("VRMLParser", VRMLParser_default_));
;// ./src/x_ite/Parser/XMLParser.js
/* provided dependency */ var XMLParser_$ = __webpack_require__(44).A;















const AccessType =
{
   initializeOnly: Base_X3DConstants .initializeOnly,
   inputOnly:      Base_X3DConstants .inputOnly,
   outputOnly:     Base_X3DConstants .outputOnly,
   inputOutput:    Base_X3DConstants .inputOutput,
};

function XMLParser (scene)
{
   Parser_X3DParser .call (this, scene, "XML Parser");

   this .protoDeclarations = [ ];
   this .parents           = [ ];
   this .parser            = new Parser_VRMLParser (scene);
   this .url               = new x_ite_Fields .MFString ();
   this .protoNames        = new Map ();
   this .protoFields       = new WeakMap ();

   this .parser .setUnits (!!scene);
}

Object .assign (Object .setPrototypeOf (XMLParser .prototype, Parser_X3DParser .prototype),
{
   getEncoding ()
   {
      return "XML";
   },
   setInput (xmlElement)
   {
      try
      {
         if (typeof xmlElement === "string")
            xmlElement = XMLParser_$.parseXML (xmlElement);

         this .input = xmlElement;
         this .xml   = this .isXML (xmlElement);

         if (!this .xml)
            Object .assign (this, HTMLParser);
      }
      catch
      {
         this .input = undefined;
      }
   },
   isValid ()
   {
      return (this .input instanceof XMLDocument) || (this .input instanceof HTMLElement) || (this .input === null);
   },
   isXML (element)
   {
      if (element instanceof HTMLElement)
         return false;
      else
         return true;
   },
   parseIntoScene (resolve, reject)
   {
      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      this .resolve = resolve;
      this .reject  = reject;

      scene .setEncoding ("XML");
      scene .setProfile (browser .getProfile ("Full"));

      this .xmlElement (this .input);
   },
   xmlElement (xmlElement)
   {
      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      if (xmlElement === null)
         return this .resolve ?.(scene);

      switch (xmlElement .nodeName)
      {
         case "#document":
         {
            const X3D = XMLParser_$(xmlElement) .children ("X3D");

            if (X3D .length)
            {
               for (const xmlElement of X3D)
                  this .x3dElement (xmlElement);
            }
            else
            {
               if (this .resolve)
               {
                  browser .loadComponents (scene) .then (() =>
                  {
                     this .childrenElements (xmlElement);
                     this .setupNodes ();
                     this .resolve (scene);
                  })
                  .catch (this .reject);
               }
               else
               {
                  this .childrenElements (xmlElement);
                  this .setupNodes ();
               }
            }

            break;
         }
         case "X3D":
         {
            this .x3dElement (xmlElement);
            break;
         }
         case "Scene":
         case "SCENE":
         {
            if (this .resolve)
            {
               browser .loadComponents (scene) .then (() =>
               {
                  this .sceneElement (xmlElement);
                  this .setupNodes ();
                  this .resolve (scene);
               })
               .catch (this .reject);
            }
            else
            {
               this .sceneElement (xmlElement);
               this .setupNodes ();
            }

            break;
         }
         default:
         {
            if (this .resolve)
            {
               browser .loadComponents (scene) .then (() =>
               {
                  this .childrenElements (xmlElement);
                  this .setupNodes ();
                  this .resolve (scene);
               })
               .catch (this .reject);
            }
            else
            {
               this .childrenElements (xmlElement);
               this .setupNodes ();
            }

            break;
         }
      }
   },
   x3dElement (xmlElement)
   {
      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      try
      {
         // Profile

         const
            profileNameId = xmlElement .getAttribute ("profile"),
            profile       = browser .getProfile (profileNameId || "Full");

         XMLParser_$.data (this .scene, "X3D", xmlElement);

         scene .setProfile (profile);
      }
      catch (error)
      {
         console .error (error);
      }

      // Specification version

      const specificationVersion = xmlElement .getAttribute ("version");

      if (specificationVersion)
         scene .setSpecificationVersion (specificationVersion);

      // Process child nodes

      for (const childNode of xmlElement .childNodes)
         this .x3dElementChildHead (childNode)

      if (!this .xml)
         this .headElement (xmlElement);

      if (this .resolve)
      {
         browser .loadComponents (scene) .then (() =>
         {
            for (const childNode of xmlElement .childNodes)
               this .x3dElementChildScene (childNode)

            this .setupNodes ();
            this .resolve (scene);
         })
         .catch (this .reject);
      }
      else
      {
         for (const childNode of xmlElement .childNodes)
            this .x3dElementChildScene (childNode)

         this .setupNodes ();
      }
   },
   x3dElementChildHead (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "head":
         case "HEAD":
            this .headElement (xmlElement);
            return;
      }
   },
   x3dElementChildScene (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "Scene":
         case "SCENE":
            this .sceneElement (xmlElement);
            return;
      }
   },
   headElement (xmlElement)
   {
      for (const childNode of xmlElement .childNodes)
         this .headElementChild (childNode);
   },
   headElementChild (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "component":
         case "COMPONENT":
            this .componentElement (xmlElement);
            return;
         case "unit":
         case "UNIT":
            this .unitElement (xmlElement);
            return;
         case "meta":
         case "META":
            this .metaElement (xmlElement);
            return;
      }
   },
   componentElement (xmlElement)
   {
      try
      {
         const
            componentNameIdCharacters = xmlElement .getAttribute ("name"),
            componentSupportLevel     = xmlElement .getAttribute ("level");

         if (componentNameIdCharacters === null)
            return console .warn ("XML Parser: Bad component statement. Expected name attribute.");

         if (componentSupportLevel === null)
            return console .warn ("XML Parser: Bad component statement. Expected level attribute.");

         const component = this .getBrowser () .getComponent (componentNameIdCharacters, parseInt (componentSupportLevel));

         this .getScene () .updateComponent (component);
      }
      catch (error)
      {
         console .log (error .message);
      }
   },
   unitElement (xmlElement)
   {
      const
         category         = xmlElement .getAttribute ("category"),
         name             = xmlElement .getAttribute ("name"),
         conversionFactor = xmlElement .getAttribute ("conversionFactor"); //works for html5 as well

      if (category === null)
         return console .warn ("XML Parser: Bad unit statement. Expected category attribute.");

      if (name === null)
         return console .warn ("XML Parser: Bad unit statement. Expected name attribute.");

      if (conversionFactor === null)
         return console .warn ("XML Parser: Bad unit statement. Expected conversionFactor attribute.");

      this .getScene () .updateUnit (category, name, parseFloat (conversionFactor));
   },
   metaElement (xmlElement)
   {
      const
         metakey   = xmlElement .getAttribute ("name"),
         metavalue = xmlElement .getAttribute ("content");

      if (metakey === null)
         return console .warn ("XML Parser: Bad meta statement. Expected name attribute.");

      if (metavalue === null)
         return console .warn ("XML Parser: Bad meta statement. Expected content attribute.");

      this .getScene () .addMetaData (metakey, metavalue);
   },
   sceneElement (xmlElement)
   {
      XMLParser_$.data (xmlElement, "node", this .scene);

      this .childrenElements (xmlElement);
   },
   childrenElements (xmlElement)
   {
      for (const childNode of xmlElement .childNodes)
         this .childElement (childNode);
   },
   childElement (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "#comment":
         case "#text":
            return;

         case "#cdata-section":
            this .cdataNode (xmlElement);
            return;

         case "ExternProtoDeclare":
         case "EXTERNPROTODECLARE":
            this .externProtoDeclareElement (xmlElement);
            return;

         case "ProtoDeclare":
         case "PROTODECLARE":
            this .protoDeclareElement (xmlElement);
            return;

         case "IS":
            this .isElement (xmlElement);
            return;

         case "ProtoInstance":
         case "PROTOINSTANCE":
            this .protoInstanceElement (xmlElement);
            return;

         case "fieldValue":
         case "FIELDVALUE":
            this .fieldValueElement (xmlElement);
            return;

         case "field":
         case "FIELD":
            this .fieldElement (xmlElement);
            return;

         case "ROUTE":
            this .routeElement (xmlElement);
            return;

         case "IMPORT":
            this .importElement (xmlElement);
            return;

         case "EXPORT":
            this .exportElement (xmlElement);
            return;

         default:
            this .nodeElement (xmlElement);
            return;
      }
   },
   externProtoDeclareElement (xmlElement)
   {
      const name = xmlElement .getAttribute ("name");

      if (this .id (name))
      {
         const
            url           = xmlElement .getAttribute ("url"),
            appInfo       = xmlElement .getAttribute ("appinfo"),
            documentation = xmlElement .getAttribute ("documentation");

         this .parser .setInput (url ?? "");
         this .parser .sfstringValues (this .url);

         if (!this .url .length)
            console .warn ("XML Parser: Bad ExternProtoDeclare statement. Expected url attribute with value.");

         const externproto = new Prototype_X3DExternProtoDeclaration (this .getExecutionContext (), this .url);

         this .pushParent (externproto);
         this .protoInterfaceElement (xmlElement);
         this .popParent ();
         this .addProtoFieldNames (externproto);

         externproto .setAppInfo (appInfo ?? "");
         externproto .setDocumentation (documentation ?? "");
         externproto .setup ();

         try
         {
            const existingExternProto = this .getExecutionContext () .getExternProtoDeclaration (name);

            console .warn (`A extern proto named '${name}' is already defined and will be overridden.`);

            this .getExecutionContext () .updateExternProtoDeclaration (this .getExecutionContext () .getUniqueExternProtoName (name), existingExternProto);
         }
         catch
         { }

         this .getExecutionContext () .updateExternProtoDeclaration (name, externproto);

         this .addProtoName (name);
      }
   },
   protoDeclareElement (xmlElement)
   {
      const
         name          = xmlElement .getAttribute ("name"),
         appInfo       = xmlElement .getAttribute ("appinfo"),
         documentation = xmlElement .getAttribute ("documentation");

      if (this .id (name))
      {
         const proto = new Prototype_X3DProtoDeclaration (this .getExecutionContext ());

         for (const childNode of xmlElement .childNodes)
         {
            switch (childNode .nodeName)
            {
               case "ProtoInterface":
               case "PROTOINTERFACE":
               {
                  this .pushParent (proto);
                  this .protoInterfaceElement (childNode);
                  this .popParent ();
                  this .addProtoFieldNames (proto);
                  break;
               }
               default:
                  continue;
            }

            break;
         }

         for (const childNode of xmlElement .childNodes)
         {
            switch (childNode .nodeName)
            {
               case "ProtoBody":
               case "PROTOBODY":
               {
                  this .pushExecutionContext (proto .getBody ());
                  this .pushParent (proto);
                  this .protoBodyElement (childNode);
                  this .popParent ();
                  this .setupNodes ();
                  this .popExecutionContext ();
                  break;
               }
               default:
                  continue;
            }

            break;
         }

         proto .setAppInfo (appInfo ?? "");
         proto .setDocumentation (documentation ?? "");
         proto .setup ();

         try
         {
            const existingProto = this .getExecutionContext () .getProtoDeclaration (name);

            console .warn (`A proto named '${name}' is already defined and will be overridden.`);

            this .getExecutionContext () .updateProtoDeclaration (this .getExecutionContext () .getUniqueProtoName (name), existingProto);
         }
         catch
         { }

         this .getExecutionContext () .updateProtoDeclaration (name, proto);

         this .addProtoName (name);
      }
   },
   protoInterfaceElement (xmlElement)
   {
      for (const childNode of xmlElement .childNodes)
         this .protoInterfaceElementChild (childNode);
   },
   protoInterfaceElementChild (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "field": // User-defined field
         case "FIELD": // User-defined field
            this .fieldElement (xmlElement);
            return;
      }
   },
   fieldElement (xmlElement)
   {
      try
      {
         if (this .getParents () .length === 0)
            return;

         const node = this .getParent ();

         if (!(node instanceof Base_X3DBaseNode))
            return;

         if (!node .canUserDefinedFields ())
            return;

         const
            accessType = AccessType [xmlElement .getAttribute ("accessType")] || Base_X3DConstants .initializeOnly,
            Field      = x_ite_Fields [xmlElement .getAttribute ("type")];

         if (!Field)
            return;

         const
            name          = xmlElement .getAttribute ("name"),
            appInfo       = xmlElement .getAttribute ("appinfo"),
            documentation = xmlElement .getAttribute ("documentation");

         if (!this .id (name))
            return;

         const field = new Field ();

         field .setAppInfo (appInfo ?? "");
         field .setDocumentation (documentation ?? "");

         if (accessType & Base_X3DConstants .initializeOnly)
         {
            this .fieldValue (field, xmlElement .getAttribute ("value"));

            this .pushParent (field);
            this .childrenElements (xmlElement);
            this .popParent ();
         }

         node .addUserDefinedField (accessType, name, field);
      }
      catch (error)
      {
         console .error (error);
      }
   },
   protoBodyElement (xmlElement)
   {
      this .childrenElements (xmlElement);
   },
   isElement (xmlElement)
   {
      if (this .isInsideProtoDeclaration ())
      {
         for (const childNode of xmlElement .childNodes)
            this .isElementChild (childNode);
      }
   },
   isElementChild (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "connect":
         case "CONNECT":
            this .connectElement (xmlElement);
            return;
      }
   },
   connectElement (xmlElement)
   {
      const
         nodeFieldName  = xmlElement .getAttribute ("nodeField"),
         protoFieldName = xmlElement .getAttribute ("protoField");

      if (nodeFieldName === null)
         return console .warn ("XML Parser: Bad connect statement. Expected nodeField attribute.");

      if (protoFieldName === null)
         return console .warn ("XML Parser: Bad connect statement. Expected protoField attribute.");

      try
      {
         if (this .getParents () .length === 0)
            return;

         const
            node  = this .getParent (),
            proto = this .getOuterNode ();

         if (!(node instanceof Core_X3DNode))
            return;

         const
            nodeField  = node .getField (nodeFieldName),
            protoField = proto .getField (protoFieldName);

         if (nodeField .getType () === protoField .getType ())
         {
            if (protoField .isReference (nodeField .getAccessType ()))
               nodeField .addReference (protoField);
            else
               throw new Error (`Field '${nodeField .getName ()}' and '${protoField .getName ()}' in PROTO ${proto .getName ()} are incompatible as an IS mapping.`);
         }
         else
            throw new Error (`Field '${nodeField .getName ()}' and '${protoField .getName ()}' in PROTO ${this .proto .getName()} have different types.`);
      }
      catch (error)
      {
         console .warn (`XML Parser: Couldn't create IS reference. ${error .message}`);
      }
   },
   protoInstanceElement (xmlElement)
   {
      try
      {
         if (this .useAttribute (xmlElement))
            return;

         const name = xmlElement .getAttribute ("name");

         if (this .id (name))
         {
            const node = this .getExecutionContext () .createProto (name, false);

            if (!node)
               throw new Error (`Unknown proto or externproto type '${name}'.`);

            ///DOMIntegration: attach node to DOM xmlElement for access from DOM.
            XMLParser_$.data (xmlElement, "node", node);

            this .defAttribute (xmlElement, node);
            this .addNode (xmlElement, node);
            this .pushParent (node);
            this .childrenElements (xmlElement);

            if (!this .isInsideProtoDeclaration ())
               this .getNodes () .push (node);

            this .popParent ();
         }
      }
      catch (error)
      {
         console .warn ("XML Parser: ", error .message);

         if (DEVELOPMENT)
            console .error (error);
      }
   },
   fieldValueElement (xmlElement)
   {
      try
      {
         if (this .getParents () .length === 0)
            return;

         const
            node = this .getParent (),
            name = xmlElement .getAttribute ("name");

         if (!(node instanceof Core_X3DPrototypeInstance))
            return;

         if (!this .id (name))
            return;

         const
            field      = node .getField (name),
            accessType = field .getAccessType ();

         if (accessType & Base_X3DConstants .initializeOnly)
         {
            if (field .getType () === Base_X3DConstants .MFNode)
            {
               field .length = 0;
            }

            this .fieldValue (field, xmlElement .getAttribute ("value"));

            this .pushParent (field);
            this .childrenElements (xmlElement);
            this .popParent ();
         }
      }
      catch (error)
      {
         console .warn (`XML Parser: Couldn't assign field value. ${error .message}`);
      }
   },
   nodeElement (xmlElement)
   {
      try
      {
         if (this .useAttribute (xmlElement))
            return;

         const node = this .getExecutionContext () .createNode (this .nodeNameToCamelCase (xmlElement .nodeName), false)
            ?? this .getExecutionContext () .createProto (this .protoNameToCamelCase (xmlElement .nodeName), false);

         if (!node)
            throw new Error (`Unknown node type '${xmlElement .nodeName}'. You probably have insufficient component/profile statements and/or an inappropriate specification version.`);

         ///DOMIntegration: attach node to DOM xmlElement for access from DOM.
         XMLParser_$.data (xmlElement, "node", node);

         //DOMIntegration: Script node support for HTML.
         if (xmlElement .nodeName === "SCRIPT")
            this .scriptElement (xmlElement);

         this .defAttribute (xmlElement, node);
         this .addNode (xmlElement, node);
         this .pushParent (node);
         this .nodeAttributes (xmlElement, node);
         this .childrenElements (xmlElement);

         if (!this .isInsideProtoDeclaration ())
            this .getNodes () .push (node);

         this .popParent ();
      }
      catch (error)
      {
         // NULL

         if (xmlElement .nodeName == "NULL")
         {
            this .addNode (xmlElement, null);
            return;
         }

         if (DEVELOPMENT)
            console .error (error);
         else
            console .error (`XML Parser: ${error .message}`);
      }
   },
   scriptElement (element)
   {
      const
         scriptDocument = XMLParser_$.parseXML (element .outerHTML, "application/xml"),
         childNodes     = scriptDocument .children [0] .childNodes;

      element .textContent = "// Content moved into childNodes.";

      for (const childNode of childNodes)
      {
         // Add elements and cdata.
         if (childNode .nodeType === 1 || childNode .nodeType === 4)
            element .appendChild (childNode);
      }
   },
   routeElement (xmlElement)
   {
      try
      {
         const
            sourceNodeName      = xmlElement .getAttribute ("fromNode"),
            sourceField         = xmlElement .getAttribute ("fromField"),
            destinationNodeName = xmlElement .getAttribute ("toNode"),
            destinationField    = xmlElement .getAttribute ("toField");

         if (sourceNodeName === null)
            throw new Error ("Bad ROUTE statement: Expected fromNode attribute.");

         if (sourceField === null)
            throw new Error ("Bad ROUTE statement: Expected fromField attribute.");

         if (destinationNodeName === null)
            throw new Error ("Bad ROUTE statement: Expected toNode attribute.");

         if (destinationField === null)
            throw new Error ("Bad ROUTE statement: Expected toField attribute.");

         const
            executionContext = this .getExecutionContext (),
            sourceNode       = executionContext .getLocalNode (sourceNodeName),
            destinationNode  = executionContext .getLocalNode (destinationNodeName),
            route            = executionContext .addRoute (sourceNode, sourceField, destinationNode, destinationField);

         ///DOMIntegration: attach node to DOM xmlElement for access from DOM.
         XMLParser_$.data (xmlElement, "node", route);
      }
      catch (error)
      {
         console .warn (`XML Parser: ${error .message}`);

         if (DEVELOPMENT)
            console .error (error);
      }
   },
   importElement (xmlElement)
   {
      try
      {
         const
            inlineNodeName   = xmlElement .getAttribute ("inlineDEF"),
            exportedNodeName = xmlElement .getAttribute ("importedDEF") || xmlElement .getAttribute ("exportedDEF"),
            localNodeName    = xmlElement .getAttribute ("AS") || exportedNodeName,
            description      = xmlElement .getAttribute ("DESCRIPTION") || "";

         if (inlineNodeName === null)
            throw new Error ("Bad IMPORT statement: Expected inlineDEF attribute.");

         if (exportedNodeName === null)
            throw new Error ("Bad IMPORT statement: Expected importedDEF attribute.");

         const inlineNode = this .getExecutionContext () .getNamedNode (inlineNodeName);

         // Rename existing imported node.

         this .renameExistingNode (localNodeName);

         // Add new imported node.

         this .getExecutionContext () .addImportedNode (inlineNode, exportedNodeName, localNodeName, description);

         if (!this .getImportedNodes () .has (localNodeName))
         {
            this .getImportedNodes () .set (localNodeName, this .getExecutionContext () .getImportedNodes () .get (localNodeName));
         }
      }
      catch (error)
      {
         console .warn (`XML Parser: ${error .message}`);
      }
   },
   exportElement (xmlElement)
   {
      try
      {
         const
            localNodeName    = xmlElement .getAttribute ("localDEF"),
            exportedNodeName = xmlElement .getAttribute ("AS") || localNodeName,
            description      = xmlElement .getAttribute ("DESCRIPTION") || "";

         if (localNodeName === null)
            throw new Error ("Bad EXPORT statement: Expected localDEF attribute.");

         const localNode = this .getExecutionContext () .getLocalNode (localNodeName);

         try
         {
            const existingNode = this .getScene () .getExportedNode (exportedNodeName);

            this .getScene () .addExportedNode (this .getScene () .getUniqueExportName (exportedNodeName), existingNode);
         }
         catch
         { }

         this .getScene () .updateExportedNode (exportedNodeName, localNode, description);
      }
      catch (error)
      {
         console .warn (`XML Parser: ${error .message}`);
      }
   },
   cdataNode (xmlElement)
   {
      if (this .getParents () .length === 0)
         return;

      const node = this .getParent ();

      if (node instanceof Core_X3DNode)
         node .getSourceText () ?.push (xmlElement .data);
   },
   useAttribute (xmlElement)
   {
      const name = xmlElement .getAttribute ("USE");

      if (this .id (name))
      {
         const
            browser  = this .getBrowser (),
            nodeName = this .nodeNameToCamelCase (xmlElement .nodeName);

         const type = XMLParser_$.try (() => nodeName === "ProtoInstance"
            ? browser .getAbstractNode ("X3DPrototypeInstance")
            : browser .getConcreteNode (nodeName))
            ?? Core_X3DNode;

         const typeName = xmlElement .getAttribute ("name");

         try
         {
            const localNode = this .getExecutionContext () .getLocalNode (name);

            const node = localNode instanceof Execution_X3DImportedNode
               ? localNode .getExportedNode (type)
               : localNode .getValue ();

            this .checkNodeType (node, name, type, typeName);
            this .addNode (xmlElement, node);
         }
         catch
         {
            const placeholder = this .getPlaceholders () .get (name);

            if (placeholder)
            {
               this .addNode (xmlElement, placeholder);
            }
            else
            {
               const placeholder = new Parser_Placeholder (this, name, type, typeName);

               this .getPlaceholders () .set (name, placeholder);
               this .addNode (xmlElement, placeholder);
            }
         }

         return true;
      }

      return false;
   },
   checkNodeType (node, name, type, typeName)
   {
      if (type === Core_X3DNode)
         return;

      if (type === Core_X3DPrototypeInstance)
      {
         if (!node .getType () .includes (Base_X3DConstants .X3DPrototypeInstance))
         {
            console .warn (`XML Parser: DEF/USE mismatch, '${name}', referenced node is not of type X3DPrototypeInstance.`);
         }
         else if (typeName !== node .getTypeName ())
         {
            console .warn (`XML Parser: DEF/USE mismatch, '${name}', name ${typeName} != ${node .getTypeName ()}.`);
         }
      }
      else if (type !== node .constructor)
      {
         console .warn (`XML Parser: DEF/USE mismatch, '${name}', ${type .typeName} != ${node .getTypeName ()}.`);
      }
   },
   defAttribute (xmlElement, node)
   {
      try
      {
         const name = xmlElement .getAttribute ("DEF");

         if (name)
         {
            if (!this .getNamedNodes () .has (name))
               this .getNamedNodes () .set (name, node);

            this .renameExistingNode (name);

            this .getExecutionContext () .updateNamedNode (name, node);
         }
      }
      catch (error)
      {
         console .warn (`XML Parser: Invalid DEF name: ${error .message}`);
      }
   },
   nodeAttributes (xmlElement, node)
   {
      for (const xmlAttribute of xmlElement .attributes)
         this .nodeAttribute (xmlAttribute, node);
   },
   nodeAttribute (xmlAttribute, node)
   {
      try
      {
         const field = node .getPredefinedField (this .attributeToCamelCase (node, xmlAttribute .name));

         if (field .isInitializable ())
            this .fieldValue (field, xmlAttribute .value);
      }
      catch (error)
      {
         // console .error (error);
      }
   },
   fieldValue (field, value)
   {
      if (value === null)
         return false;

      this .parser .pushExecutionContext (this .getExecutionContext ());
      this .parser .setInput (value);

      const ok = this [field .getType ()] .call (this .parser, field, field .getUnit ());

      this .parser .popExecutionContext ();

      return ok;
   },
   id (string)
   {
      if (string === null)
         return false;

      if (string .length === 0)
         return false;

      return true;
   },
   getParents ()
   {
      return this .parents;
   },
   getParent ()
   {
      return this .parents .at (-1);
   },
   pushParent (parent)
   {
      return this .parents .push (parent);
   },
   popParent ()
   {
      this .parents .pop ();
   },
   addNode (xmlElement, node)
   {
      if (this .parents .length === 0 || this .getParent () instanceof Prototype_X3DProtoDeclaration)
      {
         this .getExecutionContext () .rootNodes .push (node);
         return;
      }

      const parent = this .getParent ();

      if (parent instanceof Base_X3DField)
      {
         switch (parent .getType ())
         {
            case Base_X3DConstants .SFNode:
               parent .setValue (node);
               return;

            case Base_X3DConstants .MFNode:
               parent .push (node);
               return;
         }

         return;
      }

      // parent is a node.

      try
      {
         const containerField = xmlElement .getAttribute ("containerField")
            || node ?.getContainerField (this .getExecutionContext () .getSpecificationVersion ());

         if (!containerField)
         {
            console .warn ("XML Parser: Node must have a container field attribute.");
            return;
         }

         const field = parent .getField (containerField);

         switch (field .getType ())
         {
            case Base_X3DConstants .SFNode:
               field .setValue (node);
               return;

            case Base_X3DConstants .MFNode:
               field .push (node);
               return;
         }
      }
      catch (error)
      {
         console .warn (`XML Parser: Container field for node ${node ?.getTypeName () ?? node} not found. ${error .message}`);
      }
   },
   // Overloaded by HTMLParser.
   addProtoName (name)
   { },
   addProtoFieldNames (protoNode)
   { },
   protoNameToCamelCase (typeName)
   {
      return typeName;
   },
   nodeNameToCamelCase (typeName)
   {
      return typeName;
   },
   attributeToCamelCase (node, name)
   {
      return name;
   },
});

Object .assign (XMLParser .prototype,
{
   [Base_X3DConstants .SFBool]:      Parser_VRMLParser .prototype .sfboolValue,
   [Base_X3DConstants .SFColor]:     Parser_VRMLParser .prototype .sfcolorValue,
   [Base_X3DConstants .SFColorRGBA]: Parser_VRMLParser .prototype .sfcolorrgbaValue,
   [Base_X3DConstants .SFDouble]:    Parser_VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFFloat]:     Parser_VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFImage]:     Parser_VRMLParser .prototype .sfimageValue,
   [Base_X3DConstants .SFInt32]:     Parser_VRMLParser .prototype .sfint32Value,
   [Base_X3DConstants .SFMatrix3f]:  Parser_VRMLParser .prototype .sfmatrix3Value,
   [Base_X3DConstants .SFMatrix3d]:  Parser_VRMLParser .prototype .sfmatrix3Value,
   [Base_X3DConstants .SFMatrix4f]:  Parser_VRMLParser .prototype .sfmatrix4Value,
   [Base_X3DConstants .SFMatrix4d]:  Parser_VRMLParser .prototype .sfmatrix4Value,
   [Base_X3DConstants .SFNode] (field)
   {
      field .setValue (null);
      return true;
   },
   [Base_X3DConstants .SFQuaternion]: Parser_VRMLParser .prototype .sfquaternionValue,
   [Base_X3DConstants .SFRotation]:   Parser_VRMLParser .prototype .sfrotationValue,
   [Base_X3DConstants .SFString] (field)
   {
      field .setValue (x_ite_Fields .SFString .unescape (this .input));
      return true;
   },
   [Base_X3DConstants .SFTime]:      Parser_VRMLParser .prototype .sfdoubleValue,
   [Base_X3DConstants .SFVec2d]:     Parser_VRMLParser .prototype .sfvec2Value,
   [Base_X3DConstants .SFVec2f]:     Parser_VRMLParser .prototype .sfvec2Value,
   [Base_X3DConstants .SFVec3d]:     Parser_VRMLParser .prototype .sfvec3Value,
   [Base_X3DConstants .SFVec3f]:     Parser_VRMLParser .prototype .sfvec3Value,
   [Base_X3DConstants .SFVec4d]:     Parser_VRMLParser .prototype .sfvec4Value,
   [Base_X3DConstants .SFVec4f]:     Parser_VRMLParser .prototype .sfvec4Value,

   [Base_X3DConstants .VrmlMatrix]:  Parser_VRMLParser .prototype .sfmatrix4Value,

   [Base_X3DConstants .MFBool]:      Parser_VRMLParser .prototype .sfboolValues,
   [Base_X3DConstants .MFColor]:     Parser_VRMLParser .prototype .sfcolorValues,
   [Base_X3DConstants .MFColorRGBA]: Parser_VRMLParser .prototype .sfcolorValues,
   [Base_X3DConstants .MFDouble]:    Parser_VRMLParser .prototype .sfdoubleValues,
   [Base_X3DConstants .MFFloat]:     Parser_VRMLParser .prototype .sfdoubleValues,
   [Base_X3DConstants .MFImage]:     Parser_VRMLParser .prototype .sfimageValues,
   [Base_X3DConstants .MFInt32]:     Parser_VRMLParser .prototype .sfint32Values,
   [Base_X3DConstants .MFMatrix3d]:  Parser_VRMLParser .prototype .sfmatrixValues,
   [Base_X3DConstants .MFMatrix3f]:  Parser_VRMLParser .prototype .sfmatrixValues,
   [Base_X3DConstants .MFMatrix4d]:  Parser_VRMLParser .prototype .sfmatrixValues,
   [Base_X3DConstants .MFMatrix4f]:  Parser_VRMLParser .prototype .sfmatrixValues,
   [Base_X3DConstants .MFNode] (field)
   {
      field .length = 0;
      return true;
   },
   [Base_X3DConstants .MFQuaternion]: Parser_VRMLParser .prototype .sfquaternionValues,
   [Base_X3DConstants .MFRotation]:   Parser_VRMLParser .prototype .sfrotationValues,
   [Base_X3DConstants .MFString] (field)
   {
      if (Parser_VRMLParser .prototype .sfstringValues .call (this, field))
         return true;

      if (this .input)
      {
         console .warn (`XML Parser: Field '${field .getName ()}' is an MFString array and can have multiple values, so make sure to separate each individual string with double quotation marks!`);

         field .setValue ([this .input]);
         return true;
      }

      return false;
   },
   [Base_X3DConstants .MFTime]:      Parser_VRMLParser .prototype .sfdoubleValues,
   [Base_X3DConstants .MFVec2d]:     Parser_VRMLParser .prototype .sfvecValues,
   [Base_X3DConstants .MFVec2f]:     Parser_VRMLParser .prototype .sfvecValues,
   [Base_X3DConstants .MFVec3d]:     Parser_VRMLParser .prototype .sfvecValues,
   [Base_X3DConstants .MFVec3f]:     Parser_VRMLParser .prototype .sfvecValues,
   [Base_X3DConstants .MFVec4d]:     Parser_VRMLParser .prototype .sfvecValues,
   [Base_X3DConstants .MFVec4f]:     Parser_VRMLParser .prototype .sfvecValues,
});

// HTML Support

const HTMLParser =
{
   addProtoName (name)
   {
      // DOMIntegration: add uppercase versions of proto name.

      this .protoNames .set (name,                 name);
      this .protoNames .set (name .toUpperCase (), name);
   },
   addProtoFieldNames: (() =>
   {
      const reservedAttributes = new Set ();

      for (const reservedAttribute of [
         "DEF",
         "USE",
         "containerField",
      ])
      {
         reservedAttributes
            .add (reservedAttribute)
            .add (reservedAttribute .toLowerCase ());
      }

      return function (protoNode)
      {
         //DOMIntegration: handle lowercase versions of field names.

         const fields = new Map ();

         this .protoFields .set (protoNode, fields);

         for (const { name } of protoNode .getFieldDefinitions ())
         {
            if (reservedAttributes .has (name))
               continue;

            fields .set (name,                 name);
            fields .set (name .toLowerCase (), name);
         }
      };
   })(),
   protoNameToCamelCase (typeName)
   {
      //DOMIntegration: handle uppercase versions of node names.
      return this .protoNames .get (typeName);
   },
   nodeNameToCamelCase (typeName)
   {
      //DOMIntegration: handle uppercase versions of node names.
      return Parser_HTMLSupport .getNodeTypeName (typeName);
   },
   attributeToCamelCase (node, name)
   {
      //DOMIntegration: handle lowercase versions of field names.

      if (node instanceof Core_X3DPrototypeInstance)
         return this .protoFields .get (node .getProtoNode ()) .get (name);

      return Parser_HTMLSupport .getFieldName (name);
   },
};

Base_X3DField .prototype .fromXMLString = function (value, scene)
{
   const parser = new XMLParser (scene);

   if (!parser .fieldValue (this, value))
      throw new Error (`Couldn't read value for field '${this .getName ()}'.`);

   parser .setupNodes ();
};

const XMLParser_default_ = XMLParser;
;

/* harmony default export */ const Parser_XMLParser = (Namespace/* default */.A .add ("XMLParser", XMLParser_default_));
;// ./src/x_ite/Parser/JSONParser.js
/* provided dependency */ var JSONParser_$ = __webpack_require__(44).A;



function JSONParser (scene)
{
   Parser_X3DParser .call (this, scene);

   this .namespace = "http://www.web3d.org/specifications/x3d-namespace";
}

Object .assign (Object .setPrototypeOf (JSONParser .prototype, Parser_X3DParser .prototype),
{
   getEncoding ()
   {
      return "JSON";
   },
   setInput (json)
   {
      try
      {
         if (typeof json === "string")
            json = JSON .parse (json);

         this .input = json;
      }
      catch
      {
         this .input = undefined;
      }
   },
   isValid ()
   {
      if (!(this .input instanceof Object))
         return false;

      if (!Object .keys (this .input) .every (key => key === "X3D"))
         return false;

      return true;
   },
   parseIntoScene (resolve, reject)
   {
      /**
       * Load X3D JSON into an element.
       * json - the JavaScript object to convert to DOM.
       */

      const child = this .createElement ("X3D");

      this .convertToDOM (this .input, "", child);

      // Call the DOM parser.

      const parser = new Parser_XMLParser (this .getScene ());

      parser .setInput (child);
      parser .parseIntoScene (resolve, reject);

      this .getScene () .setEncoding ("JSON");
   },
   elementSetAttribute (element, key, value)
   {
      /**
       * Yet another way to set an attribute on an element.  does not allow you to
       * set JSON schema or encoding.
       */

      switch (key)
      {
         case "SON schema":
         {
            // JSON Schema
            break;
         }
         case "ncoding":
         {
            // encoding, UTF-8, UTF-16 or UTF-32
            break;
         }
         default:
         {
            if (typeof element .setAttribute === "function")
               element .setAttribute (key, value);

            break;
         }
      }
   },
   convertChildren (parentkey, object, element)
   {
      /**
       * converts children of object to DOM.
       */

      for (const key in object)
      {
         if (typeof object [key] !== "object")
            continue;

         if (isNaN (parseInt (key)))
            this .convertObject (key, object, element, parentkey .substring (1));
         else
            this .convertToDOM (object [key], key, element, parentkey .substring (1));
      }
   },
   createElement (key, containerField)
   {
      /**
       * a method to create and element with tagnam key to DOM in a namespace.  If
       * containerField is set, then the containerField is set in the elemetn.
       */

      let child;

      if (typeof this .namespace === "undefined")
      {
         child = document .createElement (key);
      }
      else
      {
         child = document .createElementNS (this .namespace, key);

         if (child === null || typeof child === "undefined")
         {
            console .error ("Trouble creating element for", key);

            child = document .createElement (key);
         }
      }

      if (typeof containerField !== "undefined")
         this .elementSetAttribute (child, "containerField", containerField);

      return child;
   },
   createCDATA (document, element, str)
   {
      const
         docu  = JSONParser_$.parseXML ("<xml></xml>", "application/xml"),
         cdata = docu .createCDATASection (str);

      element .appendChild (cdata);
   },
   convertObject (key, object, element, containerField)
   {
      /**
       * convert the object at object[key] to DOM.
       */

      if (object !== null && typeof object [key] === "object")
      {
         if (key [0] === "@")
         {
            this .convertToDOM (object [key], key, element);
         }
         else if (key [0] === "-")
         {
            this .convertChildren (key, object [key], element);
         }
         else if (key === "#comment")
         {
            for (const c in object [key])
            {
               const child = document .createComment (this .commentStringToXML (object [key] [c]));

               element .appendChild (child);
            }
         }
         else if (key === "#sourceCode" || key === "@sourceCode" || key === "#sourceText")
         {
            this .createCDATA (document, element, object [key] .join ("\n"));
         }
         else if (key === "connect" || key === "fieldValue" || key === "field" || key === "meta" || key === "component" || key === "unit")
         {
            for (const childkey in object [key])
            {
               // for each field
               if (typeof object [key] [childkey] === "object")
               {
                  const child = this .createElement (key, containerField);

                  this .convertToDOM (object [key] [childkey], childkey, child);

                  element .appendChild (child);
                  element .appendChild (document .createTextNode ("\n"));
               }
            }
         }
         else
         {
            const child = this .createElement (key, containerField);

            this .convertToDOM (object [key], key, child);

            element .appendChild (child);
            element .appendChild (document .createTextNode ("\n"));
         }
      }
   },
   commentStringToXML (str)
   {
      /**
       * convert a comment string in JavaScript to XML.  Pass the string
       */

      return str .replace (/\\\\/g, "\\");
   },
   SFStringToXML (str)
   {
      /**
       * convert an SFString to XML.
       */

      return String (str) .replace (/([\\"])/g, "\\$1");
   },
   JSONStringToXML (str)
   {
      /**
       * convert a JSON String to XML.
       */

      str = str .replace (/\\/g, "\\\\");
      str = str .replace (/\n/g, "\\n");

      return str;
   },
   convertToDOM (object, parentkey, element, containerField)
   {
      /**
       * main routine for converting a JavaScript object to DOM.
       * object is the object to convert.
       * parentkey is the key of the object in the parent.
       * element is the parent element.
       * containerField is a possible containerField.
       */

      if (object === null)
      {
         const isArray = !isNaN (parseInt (parentkey));

         if (isArray)
         {
            const child = this .createElement ("NULL", containerField);

            element .appendChild (child);
         }
         else
         {
            this .elementSetAttribute (element, "value", null);
         }

         return element;
      }

      let
         isArray        = false,
         localArray     = [ ],
         arrayOfStrings = false;

      for (const key in object)
      {
         isArray = !isNaN (parseInt (key));

         if (isArray)
         {
            switch (typeof object [key])
            {
               case "number":
               {
                  localArray .push (object [key]);
                  break;
               }
               case "string":
               {
                  localArray .push (object [key]);

                  arrayOfStrings = true;
                  break;
               }
               case "boolean":
               {
                  localArray .push (object [key]);
                  break;
               }
               case "object":
               {
                  /*
                  if (object[key] != null && typeof object[key].join === "function") {
                     localArray.push(object[key].join(" "));
                  }
                  */
                  this .convertToDOM (object [key], key, element);
                  break;
               }
               case "undefined":
               {
                  break;
               }
               default:
               {
                  console .error (`Unknown type found in array ${typeof object [key]}`);
               }
            }
         }
         else
         {
            switch (typeof object [key])
            {
               case "object":
               {
                  // This is where the whole thing starts

                  if (key === "X3D")
                     this .convertToDOM (object [key], key, element);

                  else
                     this .convertObject (key, object, element, containerField);

                  break;
               }
               case "number":
               {
                  this .elementSetAttribute (element, key .substring (1), object [key]);
                  break;
               }
               case "string":
               {
                  if (key !== "#comment")
                  {
                     // ordinary string attributes
                     this .elementSetAttribute (element, key .substring (1), this .JSONStringToXML (object [key]));
                  }
                  else
                  {
                     const child = document .createComment (this .commentStringToXML (object [key]));

                     element .appendChild (child);
                  }

                  break;
               }
               case "boolean":
               {
                  this .elementSetAttribute (element, key .substring (1), object [key]);
                  break;
               }
               case "undefined":
               {
                  break;
               }
               default:
               {
                  console .error (`Unknown type found in object ${typeof object [key]}`);
                  console .error (object);
               }
            }
         }
      }

      if (isArray)
      {
         if (parentkey [0] === "@")
         {
            if (arrayOfStrings)
            {
               arrayOfStrings = false;

               for (const [str, value] of localArray .entries ())
                  localArray [str] = this .SFStringToXML (value);

               this .elementSetAttribute (element, parentkey .substring (1), '"' + localArray .join ('" "') + '"');
            }
            else
            {
               // if non string array
               this .elementSetAttribute (element, parentkey .substring (1), localArray .join (" "));
            }
         }

         isArray = false;
      }

      return element;
   },
});

const JSONParser_default_ = JSONParser;
;

/* harmony default export */ const Parser_JSONParser = (Namespace/* default */.A .add ("JSONParser", JSONParser_default_));
;// ./src/x_ite/Parser/X3DOptimizer.js






const _skeleton = Symbol ();

function X3DOptimizer ()
{
   // Private properties

   this [_skeleton] = [ ];
}

Object .assign (X3DOptimizer .prototype,
{
   removeEmptyGroups: false,
   combineGroupingNodes: false,
   optimizeInterpolators: false,
   optimizeSceneGraph (nodes)
   {
      const removedNodes = [ ];

      nodes .setValue (this .optimizeNodes (null, nodes, true, removedNodes, new Set ()));

      removedNodes
         .filter (node => node .getValue () .getCloneCount () === 0)
         .forEach (node => node .dispose ());
   },
   optimizeNodes (parent, nodes, combine, removedNodes, seen)
   {
      return Array .from (nodes) .flatMap (node => this .optimizeNode (parent, node, combine, removedNodes, seen));
   },
   optimizeNode (parent, node, combine, removedNodes, seen)
   {
      if (!node)
         return [ ];

      if (seen .has (node))
      {
         switch (node .getNodeTypeName ())
         {
            case "HAnimJoint":
            case "HAnimSegment":
            case "HAnimSite":
            {
               if (!this [_skeleton] .at (-1))
                  return [ ];
            }
         }

         return node;
      }

      seen .add (node);

      if (this .optimizeInterpolators)
         this .removeInterpolatorsWithOnlyOneValue (node, removedNodes);

      switch (node .getNodeTypeName ())
      {
         case "Transform":
         {
            node .children = this .optimizeNodes (node, node .children, true, removedNodes, seen);

            if (this .removeEmptyGroups)
            {
               if (node .children .length === 0)
                  return [ ];
            }

            break;
         }
         case "Anchor":
         case "Group":
         {
            node .children = this .optimizeNodes (node, node .children, true, removedNodes, seen);

            return this .removeIfNoChildren (node, removedNodes);
         }
         case "Collision":
         case "LOD":
         case "Switch":
         {
            this .optimizeNodes (node, node .children, false, removedNodes, seen);

            return this .removeIfNoChildren (node, removedNodes);
         }
         case "HAnimJoint":
         case "HAnimSegment":
         case "HAnimSite":
         {
            node .children = this .optimizeNodes (node, node .children, true, removedNodes, seen);

            if (this [_skeleton] .at (-1))
            {
               return node;
            }
            else
            {
               removedNodes .push (node);

               return [ ];
            }
         }
         case "HAnimHumanoid":
         {
            this [_skeleton] .push (true);

            node .skeleton = this .optimizeNodes (node, node .skeleton, true, removedNodes, seen);

            this [_skeleton] .pop ();

            node .skin = this .optimizeNodes (node, node .skin, true, removedNodes, seen);

            return this .removeIfNoChildren (node, removedNodes);
         }
         default:
         {
            return node;
         }
      }

      if (!combine)
         return node;

      if (!this .combineGroupingNodes)
         return node;

      if (node .getValue () .hasRoutes ())
         return node;

      if (node .getNodeTypeName () === "Transform")
      {
         node = this .combineSingleChild (node, removedNodes);

         if (!node .translation ?.getValue () .equals (Numbers_Vector3 .ZERO))
            return node;

         if (!node .rotation ?.getValue () .equals (Numbers_Rotation4 .IDENTITY))
            return node;

         if (!node .scale ?.getValue () .equals (Numbers_Vector3 .ONE))
            return node;
      }

      if (!node .getNodeType () .includes (Base_X3DConstants .X3DGroupingNode))
         return node;

      const children = Array .from (node .children);

      removedNodes .push (this .removeChildren (node));

      return children;
   },
   removeChildren (node)
   {
      for (const field of node .getValue () .getFields ())
      {
         switch (field .getType ())
         {
            case Base_X3DConstants .SFNode:
               field .setValue (null);
               break;
            case Base_X3DConstants .MFNode:
               field .length = 0;
               break;
         }
      }

      return node;
   },
   removeIfNoChildren (node, removedNodes)
   {
      if (!this .removeEmptyGroups)
         return node;

      switch (node .getNodeTypeName ())
      {
         case "HAnimHumanoid":
         {
            if (node .skeleton .length || node .skin .length)
               return node;

            break;
         }
         default:
         {
            if (node .children .length !== 0)
               return node;

            break;
         }
      }

      removedNodes .push (node);

      return [ ];
   },
   removeInterpolatorsWithOnlyOneValue (node, removedNodes)
   {
      for (const field of node .getValue () .getFields ())
      {
         if (field .getInputRoutes () .size !== 1)
            continue;

         const
            route      = Array .from (field .getInputRoutes ()) [0],
            sourceNode = route .sourceNode;

         if (!sourceNode .getNodeType () .includes (Base_X3DConstants .X3DInterpolatorNode))
            continue;

         if (sourceNode .key .length !== 1)
            continue;

         node [route .destinationField] = sourceNode .keyValue [0];

         removedNodes .push (this .removeChildren (sourceNode));

         route .dispose ();
      }
   },
   combineSingleChild (node, removedNodes)
   {
      if (node .children .length !== 1)
         return node;

      if (!node .visible)
         return node;

      const child = node .children [0];

      if (child .getValue () .getCloneCount () > 1)
         return node;

      if (child .getValue () .hasRoutes ())
         return node;

      switch (child .getNodeTypeName ())
      {
         case "Transform":
         case "HAnimHumanoid":
            return this .combineTransform (node, child, removedNodes);
         case "DirectionalLight":
         case "PointLight":
         case "SpotLight":
            return this .combineLight (node, child, removedNodes);
         case "Viewpoint":
         case "OrthoViewpoint":
            return this .combineViewpoint (node, child, removedNodes);
         default:
            return node;
      }
   },
   combineTransform (node, child, removedNodes)
   {
      // Combine single Transform nodes.

      const
         translation      = new Numbers_Vector3 (),
         rotation         = new Numbers_Rotation4 (),
         scale            = new Numbers_Vector3 (),
         scaleOrientation = new Numbers_Rotation4 (),
         nodeMatrix       = new Numbers_Matrix4 (),
         childMatrix      = new Numbers_Matrix4 ();

      nodeMatrix .setTransform (node .translation .getValue (),
                                node .rotation .getValue (),
                                node .scale .getValue (),
                                node .scaleOrientation .getValue (),
                                node .center .getValue ());

      childMatrix .setTransform (child .translation .getValue (),
                                 child .rotation .getValue (),
                                 child .scale .getValue (),
                                 child .scaleOrientation .getValue (),
                                 child .center .getValue ());

      nodeMatrix .multLeft (childMatrix);

      nodeMatrix .getTransform (translation, rotation, scale, scaleOrientation, child .center .getValue ());

      child .translation      = translation;
      child .rotation         = rotation;
      child .scale            = scale;
      child .scaleOrientation = scaleOrientation;

      if (!child .getNodeTypeName () && node .getNodeTypeName ())
      {
         const executionContext = child .getExecutionContext ();

         executionContext .addNamedNode (executionContext .getUniqueName (node .getNodeTypeName ()), child);
      }

      removedNodes .push (this .removeChildren (node));

      return child;
   },
   combineLight (node, child, removedNodes)
   {
      // Combine single light nodes.

      const nodeMatrix = new Numbers_Matrix4 ();

      nodeMatrix .setTransform (node .translation .getValue (),
                                node .rotation .getValue (),
                                node .scale .getValue (),
                                node .scaleOrientation .getValue (),
                                node .center .getValue ());

      if (child .location)
         child .location = nodeMatrix .multVecMatrix (child .location .getValue ());

      if (child .direction)
         child .direction = nodeMatrix .multDirMatrix (child .direction .getValue ()) .normalize ();

      removedNodes .push (this .removeChildren (node));

      return child;
   },
   combineViewpoint (node, child, removedNodes)
   {
      // Combine single viewpoint nodes.

      const
         nodeMatrix      = new Numbers_Matrix4 (),
         viewpointMatrix = new Numbers_Matrix4 (),
         translation     = new Numbers_Vector3 (),
         rotation        = new Numbers_Rotation4 ();

      nodeMatrix .setTransform (node .translation .getValue (),
                                node .rotation .getValue (),
                                node .scale .getValue (),
                                node .scaleOrientation .getValue (),
                                node .center .getValue ());

      viewpointMatrix .setTransform (child .position .getValue (),
                                     child .orientation .getValue ());

      viewpointMatrix
         .multRight (nodeMatrix)
         .getTransform (translation, rotation);

      child .position         = translation;
      child .orientation      = rotation;
      child .centerOfRotation = nodeMatrix .multVecMatrix (child .centerOfRotation .getValue ());

      removedNodes .push (this .removeChildren (node));

      return child;
   },
   viewpointsCenterOfRotation (scene)
   {
      const
         bbox        = scene .getBBox (new Geometry_Box3 ()),
         modelMatrix = new Numbers_Matrix4 (),
         seen        = new Set ();

      this .viewpointsCenterOfRotationNodes (scene .rootNodes, bbox, modelMatrix, seen);
   },
   viewpointsCenterOfRotationNodes (nodes, bbox, modelMatrix, seen)
   {
      for (const node of nodes)
         this .viewpointsCenterOfRotationNode (node ?.getValue (), bbox, modelMatrix, seen);
   },
   viewpointsCenterOfRotationNode (node, bbox, modelMatrix, seen)
   {
      if (!node)
         return;

      if (seen .has (node))
         return;

      seen .add (node);

      if (node .getMatrix)
         modelMatrix = modelMatrix .copy () .multLeft (node .getMatrix ());

      switch (node .getType () .at (-1))
      {
         case Base_X3DConstants .Viewpoint:
         case Base_X3DConstants .OrthoViewpoint:
         {
            node ._centerOfRotation = modelMatrix .copy () .inverse () .multVecMatrix (bbox .center);
            break;
         }
      }

      for (const field of node .getFields ())
      {
         switch (field .getType ())
         {
            case Base_X3DConstants .SFNode:
               this .viewpointsCenterOfRotationNode (field .getValue (), bbox, modelMatrix, seen);
               break;
            case Base_X3DConstants .MFNode:
               this .viewpointsCenterOfRotationNodes (field, bbox, modelMatrix, seen);
               break;
         }
      }
   },
});

const X3DOptimizer_default_ = X3DOptimizer;
;

/* harmony default export */ const Parser_X3DOptimizer = (Namespace/* default */.A .add ("X3DOptimizer", X3DOptimizer_default_));
;// ./src/standard/Math/Geometry/Plane3.js



const
   point     = new Numbers_Vector3 (),
   invMatrix = new Numbers_Matrix4 ();

function Plane3 (point = Numbers_Vector3 .ZERO, normal = Numbers_Vector3 .Z_AXIS)
{
   this .normal = new Numbers_Vector3 ();

   this .set (point, normal);
}

Object .assign (Plane3 .prototype,
{
   copy ()
   {
      const copy = Object .create (Plane3 .prototype);

      copy .normal             = this .normal .copy ();
      copy .distanceFromOrigin = this .distanceFromOrigin;

      return copy;
   },
   assign (plane)
   {
      this .normal .assign (plane .normal);
      this .distanceFromOrigin = plane .distanceFromOrigin;

      return this;
   },
   equals (plane)
   {
      return this .distanceFromOrigin === plane .distanceFromOrigin && this .normal .equals (plane .normal);
   },
   set (point = Numbers_Vector3 .ZERO, normal = Numbers_Vector3 .Z_AXIS)
   {
      this .normal .assign (normal);
      this .distanceFromOrigin = normal .dot (point);
      return this;
   },
   multLeft (matrix)
   {
      // Taken from Inventor:

      // Find the point on the plane along the normal from the origin
      point .assign (this .normal) .multiply (this .distanceFromOrigin);

      // Transform the plane normal by the matrix
      // to get the new normal. Use the inverse transpose
      // of the matrix so that normals are not scaled incorrectly.
      // n' = !~m * n = n * ~m
      invMatrix .assign (matrix) .submatrix .inverse ()
         .multVecMatrix (this .normal) .normalize ();

      // Transform the point by the matrix
      matrix .multMatrixVec (point);

      // The new distance is the projected distance of the vector to the
      // transformed point onto the (unit) transformed normal. This is
      // just a dot product.
      this .distanceFromOrigin = this .normal .dot (point);

      return this;
   },
   multRight (matrix)
   {
      // Taken from Inventor:

      // Find the point on the plane along the normal from the origin
      point .assign (this .normal) .multiply (this .distanceFromOrigin);

      // Transform the plane normal by the matrix
      // to get the new normal. Use the inverse transpose
      // of the matrix so that normals are not scaled incorrectly.
      // n' = n * !~m = ~m * n
      invMatrix .assign (matrix) .submatrix .inverse ()
         .multMatrixVec (this .normal) .normalize ();

      // Transform the point by the matrix
      matrix .multVecMatrix (point);

      // The new distance is the projected distance of the vector to the
      // transformed point onto the (unit) transformed normal. This is
      // just a dot product.
      this .distanceFromOrigin = this .normal .dot (point);

      return this;
   },
   getDistanceToPoint (point)
   {
      return point .dot (this .normal) - this .distanceFromOrigin;
   },
   getPerpendicularVectorToPoint (point, result = new Numbers_Vector3 ())
   {
      return result .assign (this .normal) .multiply (-this .getDistanceToPoint (point));
   },
   getClosestPointToPoint (point, result = new Numbers_Vector3 ())
   {
      return this .getPerpendicularVectorToPoint (point, result) .add (point);
   },
   intersectsLine (line, intersection)
   {
      const { point, direction } = line;

      // Check if the line is parallel to the plane.
      const theta = direction .dot (this .normal);

      // Plane and line are parallel.
      if (theta === 0)
         return false;

      // Plane and line are not parallel. The intersection point can be calculated now.
      const t = (this .distanceFromOrigin - this .normal .dot (point)) / theta;

      intersection .assign (direction) .multiply (t) .add (point);

      return true;
   },
   toString ()
   {
      return `${this .normal}, ${this .distanceFromOrigin}`;
   },
});

const Plane3_default_ = Plane3;
;

/* harmony default export */ const Geometry_Plane3 = (Namespace/* default */.A .add ("Plane3", Plane3_default_));
;// ./src/standard/Math/Geometry/Triangle3.js
/* provided dependency */ var libtess = __webpack_require__(872);


const Triangle3 =
{
   /**
    *
    * @param {Vector3} a first point of triangle
    * @param {Vector3} b second point of triangle
    * @param {Vector3} c third point of triangle
    * @returns
    */
   area: (() =>
   {
      const
         B = new Numbers_Vector3 (),
         C = new Numbers_Vector3 ();

      return function (a, b, c)
      {
         return B .assign (b) .subtract (a) .cross (C .assign (c) .subtract (a)) .norm () / 2;
      };
   })(),
   /**
    *
    * @param {Vector3} a first point of triangle
    * @param {Vector3} b second point of triangle
    * @param {Vector3} c third point of triangle
    * @param {Vector3} normal resulting normal
    * @returns
    */
   normal (a, b, c, normal)
   {
      const
         x1 = c .x - b .x,
         y1 = c .y - b .y,
         z1 = c .z - b .z,
         x2 = a .x - b .x,
         y2 = a .y - b .y,
         z2 = a .z - b .z;

      normal .set (y1 * z2 - z1 * y2,
                   z1 * x2 - x1 * z2,
                   x1 * y2 - y1 * x2);

      return normal .normalize ();
   },
   /**
    *
    * @param {Vector3} a first point of quad
    * @param {Vector3} b second point of quad
    * @param {Vector3} c third point of quad
    * @param {Vector3} d third point of quad
    * @param {Vector3} normal resulting normal
    * @returns
    */
   quadNormal (a, b, c, d, normal)
   {
      const
         x1 = c .x - a .x,
         y1 = c .y - a .y,
         z1 = c .z - a .z,
         x2 = d .x - b .x,
         y2 = d .y - b .y,
         z2 = d .z - b .z;

      normal .set (y1 * z2 - z1 * y2,
                   z1 * x2 - x1 * z2,
                   x1 * y2 - y1 * x2);

      return normal .normalize ();
   },
   triangulatePolygon: (() =>
   {
      // Function called for each vertex of tesselator output.

      function vertexCallback (index, triangles)
      {
         triangles .push (index);
      }

      // Required in case of a combine, otherwise an empty array is returned.

      function combineCallback (coords, data, weight)
      {
         return data [0];
      }

      const tessy = new libtess .GluTesselator ();

      tessy .gluTessCallback (libtess .gluEnum .GLU_TESS_VERTEX_DATA,  vertexCallback);
      tessy .gluTessCallback (libtess .gluEnum .GLU_TESS_COMBINE,      combineCallback);
      tessy .gluTessProperty (libtess .gluEnum .GLU_TESS_WINDING_RULE, libtess .windingRule .GLU_TESS_WINDING_ODD);

      return function (polygon, triangles)
      {
         tessy .gluTessBeginPolygon (triangles);
         tessy .gluTessBeginContour ();

         for (const point of polygon)
            tessy .gluTessVertex (point, point .index);

         tessy .gluTessEndContour ();
         tessy .gluTessEndPolygon ();

         // Return array of indices.
         return triangles;
      };
   })(),
   triangulateConvexPolygon (vertices, triangles)
   {
      // Fallback: Very simple triangulation for convex polygons.

      const numVertices_1 = vertices .length - 1;

      for (let i = 1; i < numVertices_1; ++ i)
         triangles .push (vertices [0], vertices [i], vertices [i + 1]);

      return triangles;
   },
};

const Triangle3_default_ = Triangle3;
;

/* harmony default export */ const Geometry_Triangle3 = (Namespace/* default */.A .add ("Triangle3", Triangle3_default_));
;// ./src/standard/Math/Geometry/ViewVolume.js








/*
 * p7 -------- p6  far plane
 * | \         | \
 * | p3 --------- p2
 * |  |        |  |
 * |  |        |  |
 * p4 |______ p5  |
 *  \ |         \ |
 *   \|          \|
 *    p0 -------- p1  near plane
 */

function ViewVolume (... args)
{
   this .viewport = new Numbers_Vector4 ();

   this .points  = Array .from ({ length: 8 }, () => new Numbers_Vector3 ());
   // front, left, right, top, bottom, back
   this .normals = Array .from ({ length: 6 }, () => new Numbers_Vector3 ());
   this .edges   = Array .from ({ length: 8 }, () => new Numbers_Vector3 ());
   // front, left, right, top, bottom, back
   this .planes  = Array .from ({ length: 6 }, () => new Geometry_Plane3 ());

   if (args .length)
      this .set (... args);
}

Object .assign (ViewVolume .prototype,
{
   set: (() =>
   {
      const matrix = new Numbers_Matrix4 ();

      return function (projectionMatrix, viewport)
      {
         this .viewport .assign (viewport);

         const [p0, p1 ,p2, p3, p4, p5, p6, p7] = this .points;

         const
            x1 = viewport [0],
            x2 = x1 + viewport [2],
            y1 = viewport [1],
            y2 = y1 + viewport [3];

         matrix .assign (projectionMatrix) .inverse ();

         ViewVolume .unProjectPointMatrix (x1, y1, 0, matrix, viewport, p0),
         ViewVolume .unProjectPointMatrix (x2, y1, 0, matrix, viewport, p1),
         ViewVolume .unProjectPointMatrix (x2, y2, 0, matrix, viewport, p2),
         ViewVolume .unProjectPointMatrix (x1, y2, 0, matrix, viewport, p3),
         ViewVolume .unProjectPointMatrix (x1, y1, 1, matrix, viewport, p4),
         ViewVolume .unProjectPointMatrix (x2, y1, 1, matrix, viewport, p5);
         ViewVolume .unProjectPointMatrix (x2, y2, 1, matrix, viewport, p6);
         ViewVolume .unProjectPointMatrix (x1, y2, 1, matrix, viewport, p7);

         const normals = this .normals;

         Geometry_Triangle3 .normal (p0, p1, p2, normals [0]); // front
         Geometry_Triangle3 .normal (p7, p4, p0, normals [1]); // left
         Geometry_Triangle3 .normal (p6, p2, p1, normals [2]); // right
         Geometry_Triangle3 .normal (p2, p6, p7, normals [3]); // top
         Geometry_Triangle3 .normal (p1, p0, p4, normals [4]); // bottom
         Geometry_Triangle3 .normal (p4, p7, p6, normals [5]); // back

         const planes = this .planes;

         planes [0] .set (p1, normals [0]); // front
         planes [1] .set (p4, normals [1]); // left
         planes [2] .set (p2, normals [2]); // right
         planes [3] .set (p6, normals [3]); // top
         planes [4] .set (p0, normals [4]); // bottom
         planes [5] .set (p7, normals [5]); // back

         this .edges .tainted = true;

         return this;
      };
   })(),
   getViewport ()
   {
      return this .viewport;
   },
   getEdges ()
   {
      // Return suitable edges for SAT theorem.

      const edges = this .edges;

      if (edges .tainted)
      {
         const [p0, p1 ,p2, p3, p4, p5, p6, p7] = this .points;

         edges [0] .assign (p0) .subtract (p1);
         edges [1] .assign (p1) .subtract (p2);
         edges [2] .assign (p2) .subtract (p3);
         edges [3] .assign (p3) .subtract (p0);

         edges [4] .assign (p0) .subtract (p4);
         edges [5] .assign (p1) .subtract (p5);
         edges [6] .assign (p2) .subtract (p6);
         edges [7] .assign (p3) .subtract (p7);

         // Edges 8 - 11 are equal to edges 0 - 3.

         edges .tainted = false;
      }

      return edges;
   },
   intersectsSphere (radius, center)
   {
      for (const plane of this .planes)
      {
         if (plane .getDistanceToPoint (center) > radius)
            return false;
      }

      return true;
   },
   intersectsBox: (() =>
   {
      const
         points1  = Array .from ({ length: 8 }, () => new Numbers_Vector3 ()),
         normals1 = Array .from ({ length: 3 }, () => new Numbers_Vector3 ()),
         axes1    = Array .from ({ length: 3 }, () => new Numbers_Vector3 ()),
         axes     = Array .from ({ length: 3 * 8 }, () => new Numbers_Vector3 ());

      return function (box)
      {
         // Get points.

         box .getPoints (points1);

         const points2 = this .points;

         // Test the three planes spanned by the normal vectors of the faces of the box.

         if (Algorithms_SAT .isSeparated (box .getNormals (normals1), points1, points2))
            return false;

         // Test the six planes spanned by the normal vectors of the faces of the view volume.

         if (Algorithms_SAT .isSeparated (this .normals, points1, points2))
            return false;

         // Test the planes spanned by the edges of each object.

         box .getAxes (axes1);

         const edges = this .getEdges ();

         for (let i1 = 0; i1 < 3; ++ i1)
         {
            for (let i2 = 0; i2 < 8; ++ i2)
               axes [i1 * 3 + i2] .assign (axes1 [i1]) .cross (edges [i2]);
         }

         if (Algorithms_SAT .isSeparated (axes, points1, points2))
            return false;

         // Both boxes intersect.

         return true;
      };
   })(),
});

Object .assign (ViewVolume,
{
   unProjectPoint: (() =>
   {
      const invModelViewProjectionMatrix = new Numbers_Matrix4 ();

      return function (winX, winY, winZ, modelViewMatrix, projectionMatrix, viewport, point)
      {
         return this .unProjectPointMatrix (winX, winY, winZ, invModelViewProjectionMatrix .assign (modelViewMatrix) .multRight (projectionMatrix) .inverse (), viewport, point);
      };
   })(),
   unProjectPointMatrix: (() =>
   {
      const vin = new Numbers_Vector4 ();

      return function (winX, winY, winZ, invModelViewProjectionMatrix, viewport, point)
      {
         // Transformation of normalized coordinates between -1 and 1
         vin .set ((winX - viewport [0]) / viewport [2] * 2 - 1,
                   (winY - viewport [1]) / viewport [3] * 2 - 1,
                   winZ * 2 - 1,
                   1);

         //Objects coordinates
         invModelViewProjectionMatrix .multVecMatrix (vin);

         const d = 1 / vin .w;

         return point .set (vin .x * d, vin .y * d, vin .z * d, 1);
      };
   })(),
   unProjectRay: (() =>
   {
      const invModelViewProjectionMatrix = new Numbers_Matrix4 ();

      return function (winX, winY, modelViewMatrix, projectionMatrix, viewport, result)
      {
         return this .unProjectRayMatrix (winX, winY, invModelViewProjectionMatrix .assign (modelViewMatrix) .multRight (projectionMatrix) .inverse (), viewport, result);
      };
   })(),
   unProjectRayMatrix: (() =>
   {
      const
         near = new Numbers_Vector3 (),
         far  = new Numbers_Vector3 ();

      return function (winX, winY, invModelViewProjectionMatrix, viewport, result)
      {
         ViewVolume .unProjectPointMatrix (winX, winY, 0.0, invModelViewProjectionMatrix, viewport, near);
         ViewVolume .unProjectPointMatrix (winX, winY, 0.9, invModelViewProjectionMatrix, viewport, far);

         return result .setPoints (near, far);
      };
   })(),
   projectPoint: (() =>
   {
      const vin = new Numbers_Vector4 ();

      return function (point, modelViewMatrix, projectionMatrix, viewport, vOut)
      {
         if (point .length === 4)
            vin .assign (point);
         else
            vin .set (point .x, point .y, point .z, 1);

         projectionMatrix .multVecMatrix (modelViewMatrix .multVecMatrix (vin));

         const d = 1 / (2 * vin .w);

         return vOut .set ((vin .x * d + 0.5) * viewport [2] + viewport [0],
                           (vin .y * d + 0.5) * viewport [3] + viewport [1],
                           (vin .z * d + 0.5));
      };
   })(),
   projectPointMatrix: (() =>
   {
      const vin = new Numbers_Vector4 ();

      return function (point, modelViewProjectionMatrix, viewport, vOut)
      {
         if (point .length === 4)
            vin .assign (point);
         else
            vin .set (point .x, point .y, point .z, 1);

         modelViewProjectionMatrix .multVecMatrix (vin);

         const d = 1 / (2 * vin .w);

         return vOut .set ((vin .x * d + 0.5) * viewport [2] + viewport [0],
                           (vin .y * d + 0.5) * viewport [3] + viewport [1],
                           (vin .z * d + 0.5));
      };
   })(),
   projectLine: (() =>
   {
      const modelViewProjectionMatrix = new Numbers_Matrix4 ();

      return function (line, modelViewMatrix, projectionMatrix, viewport, result)
      {
         return this .projectLineMatrix (line, modelViewProjectionMatrix .assign (modelViewMatrix) .multRight (projectionMatrix), viewport, result);
      };
   })(),
   projectLineMatrix: (() =>
   {
      const
         near      = new Numbers_Vector2 (),
         far       = new Numbers_Vector2 (),
         direction = new Numbers_Vector3 ();

      return function (line, modelViewProjectionMatrix, viewport, result)
      {
         ViewVolume .projectPointMatrix (line .point, modelViewProjectionMatrix, viewport, near);
         ViewVolume .projectPointMatrix (direction .assign (line .direction) .multiply (1e9) .add (line .point), modelViewProjectionMatrix, viewport, far);

         return result .setPoints (near, far);
      };
   })(),
});

const ViewVolume_default_ = ViewVolume;
;

/* harmony default export */ const Geometry_ViewVolume = (Namespace/* default */.A .add ("ViewVolume", ViewVolume_default_));
;// ./src/x_ite/Rendering/TextureBuffer.js




function TextureBuffer ({ browser, width, height, float = false, mipMaps = false, colorTextures = 1 })
{
   const gl = browser .getContext ();

   this .context = gl;
   this .width   = width;
   this .height  = height;

   Object .defineProperty (this, "array",
   {
      get ()
      {
         const value = float ? new Float32Array (width * height * 4) : new Uint8Array (width * height * 4);

         Object .defineProperty (this, "array", { value: value });

         return value;
      },
      configurable: true,
   });

   // Get current frame buffer.

   const currentFrameBuffer = gl .getParameter (gl .FRAMEBUFFER_BINDING);

   // Create frame buffer.

   this .frameBuffer = gl .createFramebuffer ();

   gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);

   // Set draw buffers.

   gl .drawBuffers (Array .from ({ length: colorTextures }, (_, i) => gl .COLOR_ATTACHMENT0 + i));

   // Create color textures.

   this .colorTextures = [ ];

   for (let i = 0; i < colorTextures; ++ i)
   {
      this .colorTextures [i] = gl .createTexture ();

      gl .bindTexture (gl .TEXTURE_2D, this .colorTextures [i]);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S, gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T, gl .CLAMP_TO_EDGE);

      if (mipMaps)
      {
         gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .LINEAR_MIPMAP_LINEAR);
         gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .LINEAR);
      }
      else
      {
         gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
         gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
      }

      if (float)
         gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA32F, width, height, 0, gl .RGBA, gl .FLOAT, null);
      else
         gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA, width, height, 0, gl .RGBA, gl .UNSIGNED_BYTE, null);

      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0 + i, gl .TEXTURE_2D, this .colorTextures [i], 0);
   }

   // Create depth buffer.

   this .depthTexture = gl .createTexture ();

   gl .bindTexture (gl .TEXTURE_2D, this .depthTexture);

   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);

   gl .texImage2D (gl .TEXTURE_2D, 0, gl .DEPTH_COMPONENT24, width, height, 0, gl .DEPTH_COMPONENT, gl .UNSIGNED_INT, null);
   gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .DEPTH_ATTACHMENT, gl .TEXTURE_2D, this .depthTexture, 0);

   const status = gl .checkFramebufferStatus (gl .FRAMEBUFFER) === gl .FRAMEBUFFER_COMPLETE;

   // Restore previous frame buffer.

   gl .bindFramebuffer (gl .FRAMEBUFFER, currentFrameBuffer);

   // Always check that our framebuffer is ok.

   if (!status)
      throw new Error ("Couldn't create frame buffer.");
}

Object .assign (TextureBuffer .prototype,
{
   getOIT ()
   {
      return false;
   },
   getWidth ()
   {
      return this .width;
   },
   getHeight ()
   {
      return this .height;
   },
   getColorTexture (i = 0)
   {
      return this .colorTextures [i];
   },
   getDepthTexture ()
   {
      return this .depthTexture;
   },
   readPixels ()
   {
      const { context: gl, array, width, height } = this;

      gl .readPixels (0, 0, width, height, gl .RGBA, gl .UNSIGNED_BYTE, array);

      return array;
   },
   readDepth: (() =>
   {
      const
         invProjectionMatrix = new Numbers_Matrix4 (),
         point               = new Numbers_Vector3 ();

      return function (projectionMatrix, viewport, result)
      {
         const { context: gl, array, width, height } = this;

         gl .readBuffer (gl .COLOR_ATTACHMENT0);
         gl .readPixels (0, 0, width, height, gl .RGBA, gl .FLOAT, array);

         let
            winX  = 0,
            winY  = 0,
            winZ  = Number .POSITIVE_INFINITY,
            index = -1;

         for (let wy = 0, i = 0; wy < height; ++ wy)
         {
            for (let wx = 0; wx < width; ++ wx, i += 4)
            {
               const wz = array [i];

               if (wz < winZ)
               {
                  winX  = wx;
                  winY  = wy;
                  winZ  = wz;
                  index = i;
               }
            }
         }

         invProjectionMatrix .assign (projectionMatrix) .inverse ();

         Geometry_ViewVolume .unProjectPointMatrix (winX, winY, winZ, invProjectionMatrix, viewport, point);

         result .distance = -point .z;
         result .id       = array [index + 1];

         gl .readBuffer (gl .COLOR_ATTACHMENT1);
         gl .readPixels (0, 0, width, height, gl .RGBA, gl .FLOAT, array);

         result .normal .set (array [index], array [index + 1], array [index + 2]);
         // result .frontFacing = array [index + 3] ? true : false;

         return result;
      };
   })(),
   bind ()
   {
      const gl = this .context;

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);
   },
   dispose ()
   {
      const gl = this .context;

      gl .deleteFramebuffer (this .frameBuffer);

      for (const colorTexture of this .colorTextures)
         gl .deleteTexture (colorTexture);

      gl .deleteTexture (this .depthTexture);
    },
});

for (const key of Object .keys (TextureBuffer .prototype))
   Object .defineProperty (TextureBuffer .prototype, key, { enumerable: false });

const TextureBuffer_default_ = TextureBuffer;
;

/* harmony default export */ const Rendering_TextureBuffer = (Namespace/* default */.A .add ("TextureBuffer", TextureBuffer_default_));
;// ./src/x_ite/Rendering/RenderPass.js
const RenderPass =
{
   // Bits
   NONE:               0b00,
   VOLUME_SCATTER_BIT: 0b01,
   TRANSMISSION_BIT:   0b10,
   // Keys
   RENDER_KEY:         0,
   VOLUME_SCATTER_KEY: 1,
   TRANSMISSION_KEY:   2,
};

const RenderPass_default_ = RenderPass;
;

/* harmony default export */ const Rendering_RenderPass = (Namespace/* default */.A .add ("RenderPass", RenderPass_default_));
;// ./src/standard/Math/Algorithms/MergeSort.js
function MergeSort (array, compare)
{
   this .array     = array;
   this .auxiliary = [ ];

   if (compare)
      this .compare = compare;
}

Object .assign (MergeSort .prototype,
{
   compare (lhs, rhs)
   {
      return lhs < rhs;
   },
   sort (first, last)
   {
      this .mergeSort (first, last - 1);
   },
   mergeSort (lo, hi)
   {
      if (lo < hi)
      {
         const m = (lo + hi) >>> 1;
         this .mergeSort (lo, m);   // Recursion
         this .mergeSort (m + 1, hi); // Recursion
         this .merge (lo, m, hi);
      }
   },
   merge (lo, m, hi)
   {
      const { array, auxiliary, compare } = this;

      let i, j, k;

      i = 0, j = lo;
      // Copy first half of array a to auxiliary array b.
      while (j <= m)
         auxiliary [i++] = array [j++];

      i = 0; k = lo;
      // Copy back next-greatest element at each time.
      while (k < j && j <= hi)
      {
         if (compare (array [j], auxiliary [i]))
            array [k++] = array [j++];
         else
            array [k++] = auxiliary [i++];
      }

      // Copy back remaining elements of first half (if any).
      while (k < j)
         array [k++] = auxiliary [i++];
   }
});

const MergeSort_default_ = MergeSort;
;

/* harmony default export */ const Algorithms_MergeSort = (Namespace/* default */.A .add ("MergeSort", MergeSort_default_));
;// ./src/standard/Math/Geometry/Camera.js


const Camera =
{
   frustum (l, r, b, t, n, f, matrix)
   {
      const
         r_l = r - l,
         t_b = t - b,
         f_n = f - n,
         n_2 = 2 * n,

         A = (r + l) / r_l,
         B = (t + b) / t_b,
         C = -(f + n) / f_n,
         D = -(n_2 * f) / f_n,
         E = n_2 / r_l,
         F = n_2 / t_b;

      return matrix .set (E, 0, 0, 0,
                          0, F, 0, 0,
                          A, B, C, -1,
                          0, 0, D, 0);
   },
   perspective (fieldOfView, zNear, zFar, width, height, matrix)
   {
      const ratio = Math .tan (fieldOfView / 2) * zNear;

      if (width > height)
      {
         const aspect = width * ratio / height;
         return this .frustum (-aspect, aspect, -ratio, ratio, zNear, zFar, matrix);
      }
      else
      {
         const aspect = height * ratio / width;
         return this .frustum (-ratio, ratio, -aspect, aspect, zNear, zFar, matrix);
      }
   },
   perspective2 (fieldOfView, zNear, zFar, width, height, matrix)
   {
      const ratio = Math .tan (fieldOfView / 2) * zNear;

      return this .frustum (-ratio, ratio, -ratio, ratio, zNear, zFar, matrix);
   },
   ortho (l, r, b, t, n, f, matrix)
   {
      const
         r_l = r - l,
         t_b = t - b,
         f_n = f - n,

         A =  2 / r_l,
         B =  2 / t_b,
         C = -2 / f_n,
         D = -(r + l) / r_l,
         E = -(t + b) / t_b,
         F = -(f + n) / f_n;

      return matrix .set (A, 0, 0, 0,
                          0, B, 0, 0,
                          0, 0, C, 0,
                          D, E, F, 1);
   },
   orthoBox: (() =>
   {
      const
         min = new Numbers_Vector3 (),
         max = new Numbers_Vector3 ();

      return function (box, matrix)
      {
         box .getExtents (min, max);

         return this .ortho (min .x, max .x, min .y, max .y, -max .z, -min .z, matrix);
      };
   })(),
};

const Camera_default_ = Camera;
;

/* harmony default export */ const Geometry_Camera = (Namespace/* default */.A .add ("Camera", Camera_default_));
;// ./src/standard/Math/Geometry/Line3.js


function Line3 (point = Numbers_Vector3 .ZERO, direction = Numbers_Vector3 .Z_AXIS)
{
   this .point     = new Numbers_Vector3 ();
   this .direction = new Numbers_Vector3 ();

   this .set (point, direction);
}

Object .assign (Line3 .prototype,
{
   copy ()
   {
      const copy = Object .create (Line3 .prototype);

      copy .point     = this .point .copy ();
      copy .direction = this .direction .copy ();

      return copy;
   },
   assign (line)
   {
      this .point     .assign (line .point);
      this .direction .assign (line .direction);

      return this;
   },
   equals (line)
   {
      return this .point .equals (line .point) && this .direction .equals (line .direction);
   },
   set (point = Numbers_Vector3 .ZERO, direction = Numbers_Vector3 .Z_AXIS)
   {
      this .point     .assign (point);
      this .direction .assign (direction);
      return this;
   },
   setPoints (point1, point2)
   {
      this .point .assign (point1);
      this .direction .assign (point2) .subtract (point1) .normalize ();
      return this;
   },
   multMatrixLine (matrix)
   {
      matrix .multMatrixVec (this .point);
      matrix .multMatrixDir (this .direction) .normalize ();
      return this;
   },
   multLineMatrix (matrix)
   {
      matrix .multVecMatrix (this .point);
      matrix .multDirMatrix (this .direction) .normalize ();
      return this;
   },
   getClosestPointToPoint (point, result = new Numbers_Vector3 ())
   {
      const
         r = result .assign (point) .subtract (this .point),
         d = r .dot (this .direction);

      return result .assign (this .direction) .multiply (d) .add (this .point);
   },
   getClosestPointToLine: (() =>
   {
      const u = new Numbers_Vector3 ();

      return function (line, point)
      {
         const
            { point: p1, direction: d1 } = this,
            { point: p2, direction: d2 } = line;

         let t = d1 .dot (d2);

         if (Math .abs (t) >= 1)
            return false;  // lines are parallel

         u .assign (p2) .subtract (p1);

         t = (u .dot (d1) - t * u .dot (d2)) / (1 - t * t);

         point .assign (d1) .multiply (t) .add (p1);
         return true;
      };
   })(),
   getPerpendicularVectorToPoint: (() =>
   {
      const t = new Numbers_Vector3 ();

      return function (point, result = new Numbers_Vector3 ())
      {
         result .assign (this .point) .subtract (point);

         return result .subtract (t .assign (this .direction) .multiply (result .dot (this .direction)));
      };
   })(),
   getPerpendicularVectorToLine: (() =>
   {
      const
         diff   = new Numbers_Vector3 (),
         proj   = new Numbers_Vector3 (),
         d      = new Numbers_Vector3 (),
         point1 = new Numbers_Vector3 ();

      return function (line, result = new Numbers_Vector3 ())
      {
         const
            { point: p1, direction: d1 } = this,
            { point: p2, direction: d2 } = line;

         const t = d1 .dot (d2);

         if (Math .abs (t) >= 1)
         {
            // Lines are parallel

            diff .assign (p2) .subtract (p1);
            proj .assign (d1) .multiply (diff .dot (d1) / d1 .dot (d1));

            return proj .subtract (diff);
         }

         d .assign (p1) .subtract (p2);

         const
            re1 = d .dot (d1),
            re2 = d .dot (d2),
            e12 = d1 .dot (d2),
            E12 = e12 * e12;

         const
            a =  (re1 - re2 * e12) / (1 - E12),
            b = -(re2 - re1 * e12) / (1 - E12);

         point1 .assign (d1) .multiply (a);
         result .assign (d2) .multiply (b);

         return result .subtract (point1) .add (d);
      };
   })(),
   intersectsTriangle: (() =>
   {
      const
         pvec = new Numbers_Vector3 (),
         tvec = new Numbers_Vector3 ();

      return function (A, B, C, uvt)
      {
         // Find vectors for two edges sharing vert0.
         const
            edge1 = B .subtract (A),
            edge2 = C .subtract (A);

         // Begin calculating determinant - also used to calculate U parameter.
         pvec .assign (this .direction) .cross (edge2);

         // If determinant is near zero, ray lies in plane of triangle.
         const det = edge1 .dot (pvec);

         // Non culling intersection.

         if (det === 0)
            return false;

            const inv_det = 1 / det;

         // Calculate distance from vert0 to ray point.
         tvec .assign (this .point) .subtract (A);

         // Calculate U parameter and test bounds.
         const u = tvec .dot (pvec) * inv_det;

         if (u < 0 || u > 1)
            return false;

         // Prepare to test V parameter.
         const qvec = tvec .cross (edge1);

         // Calculate V parameter and test bounds.
         const v = this .direction .dot (qvec) * inv_det;

         if (v < 0 || u + v > 1)
            return false;

         //let u = edge2 .dot (qvec) * inv_det;

         uvt .u = 1 - u - v;
         uvt .v = u;
         uvt .t = v;

         return true;
      };
   })(),
   toString ()
   {
      return `${this .point}, ${this .direction}`;
   },
});

Object .assign (Line3,
{
   fromPoints (point1, point2)
   {
      const line = Object .create (Line3 .prototype);
      line .point     = point1 .copy ();
      line .direction = point2 .copy () .subtract (point1) .normalize ();
      return line;
   },
});

const Line3_default_ = Line3;
;

/* harmony default export */ const Geometry_Line3 = (Namespace/* default */.A .add ("Line3", Line3_default_));
;// ./src/standard/Math/Utility/MatrixStack.js
class MatrixStack extends Array
{
   #top = -1;

   constructor (Type)
   {
      super ();

      this .push (new Type ());
   }

   set (matrix)
   {
      this [this .#top] .assign (matrix);
   }

   get ()
   {
      return this [this .#top];
   }

   push (matrix = this [this .#top])
   {
      const top = ++ this .#top;

      if (top < this .length)
         this [top] .assign (matrix);
      else
         this [top] = matrix .copy ();
   }

   pop ()
   {
      -- this .#top;
   }

   clear ()
   {
      this .#top = 0;
   }

   size ()
   {
      return this .#top + 1;
   }

   identity ()
   {
      this [this .#top] .set ();
   }

   inverse ()
   {
      this [this .#top] .inverse ();
   }

   multRight (matrix)
   {
      this [this .#top] .multRight (matrix);
   }

   multLeft (matrix)
   {
      this [this .#top] .multLeft (matrix);
   }

   translate (vector)
   {
      this [this .#top] .translate (vector);
   }

   rotate (rotation)
   {
      this [this .#top] .rotate (rotation);
   }

   scale (vector)
   {
      this [this .#top] .scale (vector);
   }
}

const MatrixStack_default_ = MatrixStack;
;

/* harmony default export */ const Utility_MatrixStack = (Namespace/* default */.A .add ("MatrixStack", MatrixStack_default_));
;// ./src/x_ite/Rendering/X3DRenderObject.js
















const DEPTH_BUFFER_SIZE = 16;

function X3DRenderObject (executionContext)
{
   const browser = executionContext .getBrowser ();

   this .partialRenderKey         = "";
   this .renderKey                = "";
   this .renderCount              = 0;
   this .view                     = null;
   this .viewVolumes              = [ ];
   this .projectionMatrix         = new Utility_MatrixStack (Numbers_Matrix4);
   this .modelViewMatrix          = new Utility_MatrixStack (Numbers_Matrix4);
   this .viewMatrix               = new Utility_MatrixStack (Numbers_Matrix4);
   this .cameraSpaceMatrix        = new Utility_MatrixStack (Numbers_Matrix4);
   this .projectionMatrixArray    = new Float32Array (16);
   this .eyeMatrixArray           = new Float32Array (16);
   this .viewMatrixArray          = new Float32Array (16);
   this .cameraSpaceMatrixArray   = new Float32Array (16);
   this .hitRay                   = new Geometry_Line3 ();
   this .path                     = [ ];
   this .sensors                  = [[ ]];
   this .viewpointGroups          = [ ];
   this .lights                   = new Map ();
   this .globalLightsKeys         = [ ];
   this .globalLights             = [ ];
   this .localObjectsKeys         = [ ];
   this .localObjects             = [ ];
   this .globalShadows            = [ false ];
   this .globalShadow             = false;
   this .localShadows             = [ false ];
   this .localFogs                = [ null ];
   this .layouts                  = [ ];
   this .layoutRectangles         = [ ];
   this .hAnimNode                = [ null ];
   this .invHumanoidMatrix        = new Utility_MatrixStack (Numbers_Matrix4);
   this .renderedTextures         = new Set ();
   this .collisions               = [ ];
   this .collisionTime            = new Time_StopWatch ();
   this .numPointingShapes        = 0;
   this .numCollisionShapes       = 0;
   this .numDepthShapes          = 0;
   this .numOpaqueShapes          = 0;
   this .numTransparentShapes     = 0;
   this .pointingShapes           = [ ];
   this .collisionShapes          = [ ];
   this .activeCollisions         = [ ];
   this .depthShapes             = [ ];
   this .opaqueShapes             = [ ];
   this .transparentShapes        = [ ];
   this .transparencySorter       = new Algorithms_MergeSort (this .transparentShapes, (a, b) => a .distance < b .distance);
   this .renderPasses             = 0;
   this .renderPass               = Rendering_RenderPass .NONE;
   this .depthClearColor          = new Numbers_Vector4 (1, -1, -1, -1); // '1' for infinity, '-1' for no hit.
   this .speed                    = 0;
   this .depthBuffer              = new Rendering_TextureBuffer ({ browser, width: DEPTH_BUFFER_SIZE, height: DEPTH_BUFFER_SIZE, float: true, colorTextures: 2 });
}

Object .assign (X3DRenderObject .prototype,
{
   initialize ()
   {
      const browser = this .getBrowser ();

      browser .getRenderingProperties () ._Shading                 .addInterest ("set_renderKey__", this);
      browser .getRenderingProperties () ._LogarithmicDepthBuffer  .addInterest ("set_renderKey__", this);
      browser .getRenderingProperties () ._XRSession               .addInterest ("set_renderKey__", this);
      browser .getBrowserOptions () ._ColorSpace                   .addInterest ("set_renderKey__", this);
      browser .getBrowserOptions () ._OrderIndependentTransparency .addInterest ("set_renderKey__", this);
      browser .getBrowserOptions () ._ToneMapping                  .addInterest ("set_renderKey__", this);

      this .getViewpointStack ()  .addInterest ("set_renderKey__", this);

      this .set_renderKey__ ();
   },
   isIndependent ()
   {
      return true;
   },
   set_renderKey__ ()
   {
      const browser = this .getBrowser ();

      this .logarithmicDepthBuffer       = browser .getRenderingProperty ("LogarithmicDepthBuffer")
         || this .getViewpoint () .getLogarithmicDepthBuffer ();
      this .orderIndependentTransparency = browser .getBrowserOption ("OrderIndependentTransparency");

      let renderKey = "";

      renderKey += browser .getRenderingProperty ("Shading") === "FLAT" ? 1 : 0;
      renderKey += browser .getRenderingProperty ("XRSession")          ? 1 : 0;
      renderKey += this .logarithmicDepthBuffer                         ? 1 : 0;
      renderKey += this .orderIndependentTransparency                   ? 1 : 0;

      switch (browser .getBrowserOption ("ColorSpace"))
      {
         case "SRGB":
            renderKey += 0;
            break;
         default: // LINEAR_WHEN_PHYSICAL_MATERIAL
            renderKey += 1;
            break;
         case "LINEAR":
            renderKey += 2;
            break;
      }

      switch (browser .getBrowserOption ("ToneMapping"))
      {
         default: // NONE
            renderKey += 0;
            break;
         case "ACES_NARKOWICZ":
            renderKey += 1;
            break;
         case "ACES_HILL":
            renderKey += 2;
            break;
         case "ACES_HILL_EXPOSURE_BOOST":
            renderKey += 3;
            break;
         case "KHR_PBR_NEUTRAL":
            renderKey += 4;
            break;
      }

      this .partialRenderKey = renderKey;
   },
   getLogarithmicDepthBuffer ()
   {
      return this .logarithmicDepthBuffer;
   },
   getOrderIndependentTransparency ()
   {
      return this .orderIndependentTransparency;
   },
   getRenderKey ()
   {
      return this .renderKey;
   },
   getRenderCount ()
   {
      return this .renderCount;
   },
   advanceRenderCount: (() =>
   {
      let renderCount = 0;

      return function ()
      {
         if (renderCount === Number .MAX_SAFE_INTEGER)
            renderCount = 0;

         this .renderCount = ++ renderCount;
      }
   })(),
   getFramebuffers ()
   {
      return this .getBrowser () .getFramebuffers ();
   },
   getView ()
   {
      return this .view;
   },
   getViewVolumes ()
   {
      return this .viewVolumes;
   },
   getProjectionMatrix ()
   {
      return this .projectionMatrix;
   },
   getProjectionMatrixWithLimits (nearValue, farValue, viewport)
   {
      return this .getViewpoint () .getProjectionMatrixWithLimits (nearValue, farValue, viewport);
   },
   getModelViewMatrix ()
   {
      return this .modelViewMatrix;
   },
   getViewMatrix ()
   {
      return this .viewMatrix;
   },
   getCameraSpaceMatrix ()
   {
      return this .cameraSpaceMatrix;
   },
   getProjectionMatrixArray ()
   {
      return this .projectionMatrixArray;
   },
   getEyeMatrixArray ()
   {
      return this .eyeMatrixArray;
   },
   getViewMatrixArray ()
   {
      return this .viewMatrixArray;
   },
   getCameraSpaceMatrixArray ()
   {
      return this .cameraSpaceMatrixArray;
   },
   getHitRay ()
   {
      return this .hitRay;
   },
   getPath ()
   {
      return this .path;
   },
   getSensors ()
   {
      return this .sensors;
   },
   getViewpointGroups ()
   {
      return this .viewpointGroups;
   },
   getLights ()
   {
      return this .lights;
   },
   getGlobalLights ()
   {
      return this .globalLights;
   },
   getGlobalLightsKeys ()
   {
      return this .globalLightsKeys;
   },
   getLocalObjects ()
   {
      return this .localObjects;
   },
   getLocalObjectsKeys ()
   {
      return this .localObjectsKeys;
   },
   pushGlobalShadows (value)
   {
      this .globalShadows .push (value || this .globalShadows .at (-1));
   },
   getGlobalShadows ()
   {
      return this .globalShadows;
   },
   getGlobalShadow ()
   {
      return this .globalShadow;
   },
   pushLocalShadows (value)
   {
      this .localShadows .push (value || this .localShadows .at (-1));
   },
   popLocalShadows ()
   {
      this .localShadows .pop ();
   },
   setGlobalFog: (() =>
   {
      const modelViewMatrix = new Numbers_Matrix4 ();

      return function (fogNode)
      {
         if (fogNode .getFogType ())
         {
            const fogContainer = this .localFogs [0] || fogNode .getFogs () .pop ();

            modelViewMatrix .assign (fogNode .getModelMatrix ()) .multRight (this .getViewMatrix () .get ());
            fogContainer .set (fogNode, modelViewMatrix);

            this .localFogs [0] = fogContainer;
         }
         else
         {
            this .localFogs [0] = null;
         }
      };
   })(),
   getLocalFogs ()
   {
      return this .localFogs;
   },
   getLayouts ()
   {
      return this .layouts;
   },
   getLayoutRectangles ()
   {
      return this .layoutRectangles;
   },
   getHAnimNode ()
   {
      return this .hAnimNode;
   },
   getInvHumanoidMatrix ()
   {
      return this .invHumanoidMatrix;
   },
   getRenderedTextures ()
   {
      return this .renderedTextures;
   },
   getCollisions ()
   {
      return this .collisions;
   },
   getCollisionTime ()
   {
      return this .collisionTime;
   },
   getNumOpaqueShapes ()
   {
      return this .numOpaqueShapes;
   },
   getOpaqueShapes ()
   {
      return this .opaqueShapes;
   },
   getNumTransparentShapes ()
   {
      return this .numTransparentShapes;
   },
   getTransparentShapes ()
   {
      return this .transparentShapes;
   },
   getRenderPass ()
   {
      return this .renderPass;
   },
   setDepthClearColor (r, g, b, a)
   {
      this .depthClearColor .set (r, g, b, a);
   },
   constrainTranslation: (() =>
   {
      const
         plane       = new Geometry_Plane3 (),
         point       = new Numbers_Vector3 (),
         closest     = new Numbers_Vector3 (),
         constrained = new Numbers_Vector3 ();

      return function (translation, stepBack = true, slide = true)
      {
         // Constrains a *translation*, which should be added to viewpoint position to a possible value that avatar can move to. If the avatar reaches or intersects with an obstacle the translation is either constrained to slide along the wall or to stop.

         // Constrain translation when the viewer collides with an obstacle.

         const
            closestObject   = this .getClosestObject (translation),
            collisionRadius = this .getNavigationInfo () .getCollisionRadius (),
            distance        = closestObject .distance - collisionRadius;

         if (distance > 0)
         {
            // Move.

            const length = translation .norm ();

            if (length > distance)
            {
               // Collision, the avatar would intersect with the obstacle.
               // Slide along normal plane.

               point .assign (translation) .normalize () .multiply (distance);

               if (!slide)
                  return point;

               const wallFriction = Math_Algorithm .clamp (this .getBrowser () .getBrowserOption ("WallFriction"), 0, 1);

               // Project translation on normal plane.
               plane .set (point, closestObject .normal);
               plane .getClosestPointToPoint (translation, closest);

               // Project translation on up-vector plane.
               plane .set (Numbers_Vector3 .ZERO, this .getViewpoint () .getUpVector ());
               plane .getClosestPointToPoint (closest, constrained);

               // Adjust length.
               constrained .subtract (point) .normalize () .multiply (length * (1 - wallFriction));

               return this .constrainTranslation (constrained, false, false);
            }

            // Everything is fine.

            return translation;
         }

         // Collision, the avatar is already within an obstacle.

         if (stepBack)
         {
            point .assign (translation) .normalize () .multiply (distance);

            return this .constrainTranslation (point, false, false);
         }

         return translation .assign (Numbers_Vector3 .ZERO);
      };
   })(),
   getClosestObject: (() =>
   {
      const
         projectionMatrix     = new Numbers_Matrix4 (),
         viewProjectionMatrix = new Numbers_Matrix4 (),
         viewMatrix           = new Numbers_Matrix4 (),
         localOrientation     = new Numbers_Rotation4 (),
         rotation             = new Numbers_Rotation4 ();

      return function (direction)
      {
         ///  Returns the distance to the closest object in @a direction.  The maximum determinable value is avatarHeight * 2.

         this .collisionTime .start ();

         const
            navigationInfoNode = this .getNavigationInfo (),
            viewpointNode      = this .getViewpoint (),
            collisionRadius    = navigationInfoNode .getCollisionRadius (),
            bottom             = navigationInfoNode .getStepHeight () - navigationInfoNode .getAvatarHeight (),
            nearValue          = viewpointNode .getNearDistance (navigationInfoNode),
            avatarHeight       = navigationInfoNode .getAvatarHeight ();

         // Determine width and height of camera

         // Reshape camera

         Geometry_Camera .ortho (-collisionRadius,
                        collisionRadius,
                        Math .min (bottom, -collisionRadius), /// TODO: bottom could be a positive value if stepHeight > avatarHeight.
                        collisionRadius,
                        nearValue,
                        Math .max (collisionRadius * 2, avatarHeight * 2),
                        projectionMatrix);

         // Translate camera to user position and to look in the direction of the direction.

         localOrientation
            .assign (viewpointNode ._orientation .getValue ())
            .inverse ()
            .multRight (viewpointNode .getOrientation ());

         rotation
            .setVectors (Numbers_Vector3 .NEGATIVE_Z_AXIS, direction)
            .multRight (localOrientation);

         viewpointNode .straightenHorizon (rotation);

         viewMatrix
            .assign (viewpointNode .getModelMatrix ())
            .translate (viewpointNode .getUserPosition ())
            .rotate (rotation)
            .inverse ()
            .multLeft (viewpointNode .getCameraSpaceMatrix ());

         viewProjectionMatrix
            .assign (viewMatrix)
            .multRight (projectionMatrix);

         this .getProjectionMatrix () .push (viewProjectionMatrix);

         const closestObject = this .getCollisionShape (projectionMatrix);

         closestObject .modelViewMatrix .multRight (viewMatrix) .submatrix
            .inverse () .multMatrixVec (closestObject .normal);

         rotation .multVecRot (closestObject .normal) .normalize ();

         this .getProjectionMatrix () .pop ();

         this .collisionTime .stop ();

         return closestObject;
      };
   })(),
   getCollisionShape: (() =>
   {
      const
         depthBufferViewport   = new Numbers_Vector4 (0, 0, DEPTH_BUFFER_SIZE, DEPTH_BUFFER_SIZE),
         depthBufferViewVolume = new Geometry_ViewVolume (Numbers_Matrix4 .IDENTITY, depthBufferViewport),
         result                = { id: -1, distance: 0, normal: new Numbers_Vector3 (), modelViewMatrix: new Numbers_Matrix4 () };

      return function (projectionMatrix)
      {
         // Returns the depth value to the closest object.
         // The maximum determinable value is avatarHeight * 2.

         this .depthBuffer .bind ();
         this .viewVolumes .push (depthBufferViewVolume);

         this .depth (this .collisionShapes, this .numCollisionShapes, true);

         const depth = this .depthBuffer .readDepth (projectionMatrix, depthBufferViewport, result);

         this .viewVolumes .pop ();

         if (depth .id < 0)
         {
            depth .node = null;
         }
         else
         {
            const renderContext = this .collisionShapes [depth .id];

            depth .node = renderContext .shapeNode;
            depth .modelViewMatrix .assign (renderContext .modelViewMatrix);
         }

         return depth;
      };
   })(),
   render (type, callback, group)
   {
      switch (type)
      {
         case Rendering_TraverseType .POINTER:
         {
            this .numPointingShapes = 0;

            callback .call (group, type, this);
            this .pointing (this .pointingShapes, this .numPointingShapes);
            break;
         }
         case Rendering_TraverseType .COLLISION:
         {
            // Collect for collide and gravitate
            this .numCollisionShapes = 0;

            callback .call (group, type, this);
            this .collide ();
            this .gravitate ();
            break;
         }
         case Rendering_TraverseType .DEPTH:
         case Rendering_TraverseType .SHADOW:
         {
            this .numDepthShapes = 0;

            callback .call (group, type, this);
            this .depth (this .depthShapes, this .numDepthShapes, false);
            break;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            this .lightIndex           = 0;
            this .renderPasses         = 0;
            this .numOpaqueShapes      = 0;
            this .numTransparentShapes = 0;

            this .setGlobalFog (this .getFog ());

            callback .call (group, type, this);
            this .draw ();
            break;
         }
      }
   },
   setHitRay (projectionMatrix, viewport, pointer)
   {
      Geometry_ViewVolume .unProjectRay (pointer .x, pointer .y, Numbers_Matrix4 .IDENTITY, projectionMatrix, viewport, this .hitRay);
   },
   addPointingShape: (() =>
   {
      const
         bboxSize   = new Numbers_Vector3 (),
         bboxCenter = new Numbers_Vector3 ();

      return function (shapeNode)
      {
         const modelViewMatrix = this .getModelViewMatrix () .get ();

         modelViewMatrix .multDirMatrix (bboxSize   .assign (shapeNode .getBBoxSize ()));
         modelViewMatrix .multVecMatrix (bboxCenter .assign (shapeNode .getBBoxCenter ()));

         const
            radius     = bboxSize .norm () / 2,
            viewVolume = this .viewVolumes .at (-1);

         if (!viewVolume .intersectsSphere (radius, bboxCenter))
            return false;

         const num = this .numPointingShapes ++;

         if (num === this .pointingShapes .length)
         {
            const renderContext = {
               renderObject: this,
               modelViewMatrix: new Float32Array (16),
               viewport: new Numbers_Vector4 (),
               clipPlanes: [ ],
               sensors: [ ],
               get renderContext () { return this; },
            };

            this .pointingShapes .push (renderContext);
         }

         const renderContext = this .pointingShapes [num];

         renderContext .viewport .assign (viewVolume .getViewport ());
         renderContext .modelViewMatrix .set (modelViewMatrix);
         renderContext .hAnimNode = this .hAnimNode .at (-1);
         renderContext .shapeNode = shapeNode;

         // Clip planes & sensors

         X3DRenderObject_assign (renderContext .clipPlanes, this .localObjects);
         X3DRenderObject_assign (renderContext .sensors,    this .sensors .at (-1));

         return true;
      };
   })(),
   addCollisionShape: (() =>
   {
      const
         bboxSize   = new Numbers_Vector3 (),
         bboxCenter = new Numbers_Vector3 ();

      return function (shapeNode)
      {
         const modelViewMatrix = this .getModelViewMatrix () .get ();

         modelViewMatrix .multDirMatrix (bboxSize   .assign (shapeNode .getBBoxSize ()));
         modelViewMatrix .multVecMatrix (bboxCenter .assign (shapeNode .getBBoxCenter ()));

         const
            radius     = bboxSize .norm () / 2,
            viewVolume = this .viewVolumes .at (-1);

         if (!viewVolume .intersectsSphere (radius, bboxCenter))
            return false;

         const num = this .numCollisionShapes ++;

         if (num === this .collisionShapes .length)
         {
            const renderContext = {
               renderObject: this,
               viewport: new Numbers_Vector4 (),
               clipPlanes: [ ],
               collisions: [ ],
               modelViewMatrix: new Float32Array (16),
               get renderContext () { return this; },
            };

            this .collisionShapes .push (renderContext);
         }

         const renderContext = this .collisionShapes [num];

         renderContext .viewport .assign (viewVolume .getViewport ());
         renderContext .modelViewMatrix .set (modelViewMatrix);
         renderContext .hAnimNode = this .hAnimNode .at (-1);
         renderContext .shapeNode = shapeNode;

         // Clip planes & Collision nodes

         X3DRenderObject_assign (renderContext .clipPlanes, this .localObjects);
         X3DRenderObject_assign (renderContext .collisions, this .collisions);

         return true;
      };
   })(),
   addDepthShape: (() =>
   {
      const
         bboxSize   = new Numbers_Vector3 (),
         bboxCenter = new Numbers_Vector3 ();

      return function (shapeNode)
      {
         const modelViewMatrix = this .getModelViewMatrix () .get ();

         modelViewMatrix .multDirMatrix (bboxSize   .assign (shapeNode .getBBoxSize ()));
         modelViewMatrix .multVecMatrix (bboxCenter .assign (shapeNode .getBBoxCenter ()));

         const
            radius     = bboxSize .norm () / 2,
            viewVolume = this .viewVolumes .at (-1);

         if (!viewVolume .intersectsSphere (radius, bboxCenter))
            return false;

         const num = this .numDepthShapes ++;

         if (num === this .depthShapes .length)
         {
            const renderContext = {
               renderObject: this,
               viewport: new Numbers_Vector4 (),
               clipPlanes: [ ],
               modelViewMatrix: new Float32Array (16),
               get renderContext () { return this; },
            };

            this .depthShapes .push (renderContext);
         }

         const renderContext = this .depthShapes [num];

         renderContext .viewport .assign (viewVolume .getViewport ());
         renderContext .modelViewMatrix .set (modelViewMatrix);
         renderContext .hAnimNode = this .hAnimNode .at (-1);
         renderContext .shapeNode = shapeNode;

         // Clip planes

         X3DRenderObject_assign (renderContext .clipPlanes, this .localObjects);

         return true;
      };
   })(),
   addDisplayShape: (() =>
   {
      const
         bboxSize   = new Numbers_Vector3 (),
         bboxCenter = new Numbers_Vector3 ();

      return function (shapeNode)
      {
         const modelViewMatrix = this .getModelViewMatrix () .get ();

         modelViewMatrix .multDirMatrix (bboxSize   .assign (shapeNode .getBBoxSize ()));
         modelViewMatrix .multVecMatrix (bboxCenter .assign (shapeNode .getBBoxCenter ()));

         const
            radius     = bboxSize .norm () / 2,
            viewVolume = this .viewVolumes .at (-1);

         if (!viewVolume .intersectsSphere (radius, bboxCenter))
            return false;

         let renderContext;

         if (shapeNode .isTransparent ())
         {
            const num = this .numTransparentShapes ++;

            if (num === this .transparentShapes .length)
               this .transparentShapes .push (this .createRenderContext (true));

            renderContext = this .transparentShapes [num];

            renderContext .distance = bboxCenter .z;
         }
         else
         {
            const num = this .numOpaqueShapes ++;

            if (num === this .opaqueShapes .length)
               this .opaqueShapes .push (this .createRenderContext (false));

            renderContext = this .opaqueShapes [num];
         }

         this .renderPasses |= shapeNode .getRenderPasses ();

         renderContext .viewport .assign (viewVolume .getViewport ());
         renderContext .modelViewMatrix .set (modelViewMatrix);

         renderContext .shadows        = this .localShadows .at (-1);
         renderContext .fogNode        = this .localFogs .at (-1);
         renderContext .hAnimNode      = this .hAnimNode .at (-1);
         renderContext .shapeNode      = shapeNode;
         renderContext .appearanceNode = shapeNode .getAppearance ();

         // Clip planes and local lights

         X3DRenderObject_assign (renderContext .localObjects,     this .localObjects); // ClipPane, X3DLightNode
         X3DRenderObject_assign (renderContext .localObjectsKeys, this .localObjectsKeys);

         return true;
      };
   })(),
   createRenderContext (transparent)
   {
      return {
         renderObject: this,
         transparent: transparent,
         viewport: new Numbers_Vector4 (),
         modelViewMatrix: new Float32Array (16),
         localObjects: [ ],
         localObjectsKeys: [ ], // [clip planes, lights]
         get renderContext () { return this; },
      };
   },
   pointing (shapes, numShapes)
   {
      const
         browser               = this .getBrowser (),
         gl                    = browser .getContext (),
         projectionMatrixArray = this .projectionMatrixArray,
         { x, y }              = browser .getPointer ();

      // Configure depth shaders.

      projectionMatrixArray .set (this .getProjectionMatrix () .get ());

      // Configure framebuffer.

      gl .scissor (0, 0, 1, 1);
      gl .clear (gl .DEPTH_BUFFER_BIT);

      // Render all objects.

      gl .disable (gl .CULL_FACE);

      for (let s = 0; s < numShapes; ++ s)
      {
         const
            { renderContext, viewport, modelViewMatrix, shapeNode, hAnimNode, clipPlanes } = shapes [s],
            appearanceNode      = shapeNode .getAppearance (),
            geometryContext     = shapeNode .getGeometryContext (),
            depthModeNode       = appearanceNode .getDepthMode (),
            stylePropertiesNode = appearanceNode .getStyleProperties (geometryContext .geometryType),
            shaderNode          = browser .getPointingShader (clipPlanes .length, shapeNode, hAnimNode),
            id                  = browser .addPointingShape (renderContext);

         gl .viewport (viewport .x - x,
                       viewport .y - y,
                       viewport .z,
                       viewport .w);

         // Draw shape.

         shaderNode .enable (gl);
         shaderNode .setClipPlanes (gl, clipPlanes);

         gl .uniformMatrix4fv (shaderNode .x3d_ProjectionMatrix, false, projectionMatrixArray);
         gl .uniformMatrix4fv (shaderNode .x3d_ModelViewMatrix,  false, modelViewMatrix);
         gl .uniform1f (shaderNode .x3d_Id, id);

         depthModeNode       ?.enable (gl);
         stylePropertiesNode ?.setShaderUniforms (gl, shaderNode);
         hAnimNode           ?.setShaderUniforms (gl, shaderNode);

         shapeNode .displaySimple (gl, renderContext, shaderNode);

         depthModeNode ?.disable (gl);
         browser .resetTextureUnits ();
      }
   },
   collide: (() =>
   {
      const axes = [
         Numbers_Vector3 .X_AXIS,
         Numbers_Vector3 .Y_AXIS,
         Numbers_Vector3 .Z_AXIS,
         Numbers_Vector3 .NEGATIVE_X_AXIS,
         Numbers_Vector3 .NEGATIVE_Y_AXIS,
         Numbers_Vector3 .NEGATIVE_Z_AXIS,
      ];

      const
         closestShapes    = new Set (),
         activeCollisions = [ ]; // current active Collision nodes

      return function ()
      {
         const browser = this .getBrowser ();

         // Check if there are enabled Collision nodes.
         if (!browser .getCollisionCount ())
            return;

         // Collision nodes are handled here.

         const collisionRadius = this .getNavigationInfo () .getCollisionRadius () * Math .SQRT2;

         if (this .numCollisionShapes)
         {
            for (const axis of axes)
            {
               const closestObject = this .getClosestObject (axis);

               if (closestObject .id < 0)
                  continue;

               if (closestObject .distance > collisionRadius)
                  continue;

               closestShapes .add (closestObject .id);
            }

            for (const i of closestShapes)
            {
               const { collisions } = this .collisionShapes [i];

               for (const collision of collisions)
                  activeCollisions .push (collision);
            }

            closestShapes .clear ();
         }

         // Set isActive to FALSE for affected nodes.

         for (const collision of this .activeCollisions)
         {
            if (activeCollisions .includes (collision))
               continue;

            collision .set_active__ (false);
         }

         // Set isActive to TRUE for affected nodes.

         for (const collision of activeCollisions)
            collision .set_active__ (true);

         X3DRenderObject_assign (this .activeCollisions, activeCollisions);

         activeCollisions .length = 0;
      };
   })(),
   gravitate: (() =>
   {
      const
         projectionMatrix     = new Numbers_Matrix4 (),
         viewProjectionMatrix = new Numbers_Matrix4 (),
         translation          = new Numbers_Vector3 (),
         rotation             = new Numbers_Rotation4 ();

      return function ()
      {
         const browser = this .getBrowser ();

         // Terrain following and gravitation.

         if (browser .getActiveLayer () === this)
         {
            if (browser .getCurrentViewer () !== "WALK")
               return;
         }
         else
         {
            return;
         }

         // Get NavigationInfo values.

         const
            navigationInfoNode = this .getNavigationInfo (),
            viewpointNode      = this .getViewpoint (),
            collisionRadius    = navigationInfoNode .getCollisionRadius (),
            avatarHeight       = navigationInfoNode .getAvatarHeight (),
            stepHeight         = navigationInfoNode .getStepHeight (),
            nearValue          = viewpointNode .getNearDistance (navigationInfoNode);

         // Reshape viewpoint for gravitate.

         Geometry_Camera .ortho (-collisionRadius,
                        collisionRadius,
                        -collisionRadius,
                        collisionRadius,
                        nearValue,
                        Math .max (collisionRadius, avatarHeight) * 2,
                        projectionMatrix);

         // Transform viewpoint to look down the up vector.

         const
            upVector = viewpointNode .getUpVector (),
            down     = rotation .setVectors (Numbers_Vector3 .Z_AXIS, upVector);

         viewProjectionMatrix
            .assign (viewpointNode .getModelMatrix ())
            .translate (viewpointNode .getUserPosition ())
            .rotate (down)
            .inverse ()
            .multRight (projectionMatrix)
            .multLeft (viewpointNode .getCameraSpaceMatrix ());

         this .getProjectionMatrix () .push (viewProjectionMatrix);

         const depth = this .getCollisionShape (projectionMatrix);

         let distance = depth .distance;

         this .getProjectionMatrix () .pop ();

         // gravitate or step up.

         distance -= avatarHeight;

         const up = rotation .setVectors (Numbers_Vector3 .Y_AXIS, upVector);

         if (distance > 0)
         {
            // Gravitate and fall down the to the floor.

            const currentFrameRate = this .speed ? browser .getCurrentFrameRate () : 1000000;

            this .speed -= browser .getBrowserOptions () ._Gravity .getValue () / currentFrameRate;

            let dy = this .speed / currentFrameRate;

            if (dy < -distance)
            {
               // The ground has been reached.

               dy = -distance;

               this .speed = 0;
            }

            const falling = up .multVecRot (translation .set (0, dy, 0));

            viewpointNode ._positionOffset = falling .add (viewpointNode ._positionOffset .getValue ());
         }
         else
         {
            this .speed = 0;

            distance = -distance;

            if (distance > 0.01 && distance < stepHeight)
            {
               // Step up.

               const step = up .multVecRot (translation .set (0, distance, 0));

               viewpointNode ._positionOffset = step .add (viewpointNode ._positionOffset .getValue ());
            }
         }
      };
   })(),
   depth (shapes, numShapes, normal)
   {
      const
         browser               = this .getBrowser (),
         gl                    = browser .getContext (),
         viewport              = this .viewVolumes .at (-1) .getViewport (),
         projectionMatrixArray = this .projectionMatrixArray;

      // Configure depth shaders.

      projectionMatrixArray .set (this .getProjectionMatrix () .get ());

      // Configure viewport and background.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      gl .clearColor (... this .depthClearColor);
      gl .clear (gl .COLOR_BUFFER_BIT | gl .DEPTH_BUFFER_BIT);

      // Render all objects

      gl .disable (gl .CULL_FACE);

      for (let s = 0; s < numShapes; ++ s)
      {
         const
            { renderContext, clipPlanes, modelViewMatrix, shapeNode, hAnimNode } = shapes [s],
            appearanceNode      = shapeNode .getAppearance (),
            geometryContext     = shapeNode .getGeometryContext (),
            stylePropertiesNode = appearanceNode .getStyleProperties (geometryContext .geometryType),
            shaderNode          = browser .getDepthShader (normal, clipPlanes .length, shapeNode, hAnimNode);

         // Cannot change viewport here, because the viewport is special here.
         // gl .viewport (... viewport);

         // Draw

         shaderNode .enable (gl);
         shaderNode .setClipPlanes (gl, clipPlanes);

         gl .uniform1i (shaderNode .x3d_Id, s);
         gl .uniformMatrix4fv (shaderNode .x3d_ProjectionMatrix, false, projectionMatrixArray);
         gl .uniformMatrix4fv (shaderNode .x3d_ModelViewMatrix,  false, modelViewMatrix);

         stylePropertiesNode ?.setShaderUniforms (gl, shaderNode);
         hAnimNode           ?.setShaderUniforms (gl, shaderNode);

         shapeNode .displaySimple (gl, renderContext, shaderNode);
         browser .resetTextureUnits ();
      }
   },
   draw ()
   {
      const
         browser          = this .getBrowser (),
         gl               = browser .getContext (),
         pose             = browser .getPose (),
         framebuffers     = this .getFramebuffers (),
         numFramebuffers  = framebuffers .length,
         viewport         = this .viewVolumes .at (-1) .getViewport (),
         lights           = this .lights,
         globalLightsKeys = this .globalLightsKeys,
         globalLightsKey  = globalLightsKeys .join (""),
         globalLights     = this .globalLights,
         renderedTextures = this .renderedTextures,
         globalShadows    = this .globalShadows,
         headlight        = this .getNavigationInfo () ._headlight .getValue ();

      // PREPARATIONS

      // Render shadow maps and prepare texture projectors.
      // This must be done before rendered textures are updated.

      for (const light of lights .values ())
         light .renderShadowMap (this);

      // Render GeneratedCubeMapTexture and RenderedTexture nodes.

      for (const renderedTexture of renderedTextures)
         renderedTexture .renderTexture (this);

      this .globalShadow = globalShadows .at (-1);

      // Set matrices after shadows or other renderings.

      this .viewMatrixArray        .set (this .getViewMatrix () .get ());
      this .cameraSpaceMatrixArray .set (this .getCameraSpaceMatrix () .get ());

      // DRAW

      // Sort transparent shapes.

      if (!this .orderIndependentTransparency)
      {
         const { numTransparentShapes, transparencySorter } = this;

         transparencySorter .sort (0, numTransparentShapes);
      }

      // Draw to all framebuffers.

      for (let i = 0; i < numFramebuffers; ++ i)
      {
         this .advanceRenderCount ();

         // Set matrices with XR support.

         const view = this .view = pose ?.views [i];

         if (view)
         {
            this .projectionMatrixArray .set (view .projectionMatrix);
            this .eyeMatrixArray        .set (view .matrix);
         }
         else
         {
            this .projectionMatrixArray .set (this .getProjectionMatrix () .get ());
            this .eyeMatrixArray        .set (Numbers_Matrix4 .IDENTITY);
         }

         // Set up shadow matrix for all lights, and matrices for all projective textures.

         if (headlight)
            browser .getHeadlight () .setGlobalVariables (this);

         for (const light of lights .values ())
            light .setGlobalVariables (this);

         // Render transmission texture and volume scatter texture.

         if (this .renderPasses !== Rendering_RenderPass .NONE)
         {
            // Render to volume scatter buffer.

            if (this .renderPasses & Rendering_RenderPass .VOLUME_SCATTER_BIT)
            {
               browser .resetShapeId ();

               this .renderPass = Rendering_RenderPass .VOLUME_SCATTER_KEY;
               this .renderKey  = `.${this .partialRenderKey}.${this .renderPass}.${globalLightsKey}.`;

               const volumeScatterBuffer = browser .getVolumeScatterBuffer ();

               this .drawShapes (Rendering_RenderPass .VOLUME_SCATTER_KEY, gl, volumeScatterBuffer, gl .COLOR_BUFFER_BIT, viewport);
            }

            // Render to transmission buffer.

            if (this .renderPasses & Rendering_RenderPass .TRANSMISSION_BIT)
            {
               this .renderPass = Rendering_RenderPass .TRANSMISSION_KEY;
               this .renderKey  = `.${this .partialRenderKey}.${this .renderPass}.${globalLightsKey}.`;

               const transmissionBuffer = browser .getTransmissionBuffer ();

               this .drawShapes (Rendering_RenderPass .TRANSMISSION_KEY, gl, transmissionBuffer, gl .COLOR_BUFFER_BIT, viewport);

               // Mipmap is later selected based on roughness and ior.
               gl .bindTexture (gl .TEXTURE_2D, transmissionBuffer .getColorTexture ());
               gl .generateMipmap (gl .TEXTURE_2D);
            }
         }

         // Draw with sorted blend or OIT.

         this .renderPass = Rendering_RenderPass .RENDER_KEY;
         this .renderKey  = `.${this .partialRenderKey}.${this .renderPass}.${globalLightsKey}.`;

         const frameBuffer = framebuffers [i];

         this .drawShapes (Rendering_RenderPass .RENDER_KEY, gl, frameBuffer, 0, viewport);
      }

      this .view = null;

      // POST DRAW

      if (this .isIndependent ())
      {
         // Recycle clip planes, local fogs, local lights, and local projective textures.

         const localObjects = browser .getLocalObjects ();

         for (const localObject of localObjects)
            localObject .dispose ();

         localObjects .clear ();

         // Recycle global lights and global projective textures.

         for (const globalObject of globalLights)
            globalObject .dispose ();

         browser .resetGlobalTextureUnits ();
      }
      else
      {
         for (const light of lights .values ())
            light .modelViewMatrix .pop ();
      }

      // Reset containers.

      globalLightsKeys .length = 0;
      globalLights     .length = 0;
      globalShadows    .length = 1;

      lights           .clear ();
      renderedTextures .clear ();
   },
   drawShapes (renderPass, gl, frameBuffer, clearBits, viewport)
   {
      const { opaqueShapes, numOpaqueShapes, transparentShapes, numTransparentShapes } = this;

      frameBuffer .bind ();

      // Configure viewport and background.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Draw background.

      gl .clearColor (0, 0, 0, 0);
      gl .clear (gl .DEPTH_BUFFER_BIT | clearBits);
      gl .blendFuncSeparate (gl .SRC_ALPHA, gl .ONE_MINUS_SRC_ALPHA, gl .ONE, gl .ONE_MINUS_SRC_ALPHA);

      this .getBackground () [renderPass] ?.display (gl, this);

      // Use sorted blend or order independent transparency.
      // Render opaque objects first.

      for (let i = 0; i < numOpaqueShapes; ++ i)
      {
         const { renderContext, shapeNode } = opaqueShapes [i];

         shapeNode [renderPass] ?.display (gl, renderContext);
      }

      // Render transparent objects.

      if (frameBuffer .getOIT ())
         frameBuffer .bindTransparency ();

      gl .depthMask (false);
      gl .enable (gl .BLEND);

      for (let i = 0; i < numTransparentShapes; ++ i)
      {
         const { renderContext, shapeNode } = transparentShapes [i];

         shapeNode [renderPass] ?.display (gl, renderContext);
      }

      gl .depthMask (true);
      gl .disable (gl .BLEND);

      if (frameBuffer .getOIT ())
         frameBuffer .compose ();
   },
});

function X3DRenderObject_assign (lhs, rhs)
{
   const length = rhs .length;

   for (let i = 0; i < length; ++ i)
      lhs [i] = rhs [i];

   lhs .length = length;
}

X3DRenderObject .assign = X3DRenderObject_assign;

const X3DRenderObject_default_ = X3DRenderObject;
;

/* harmony default export */ const Rendering_X3DRenderObject = (Namespace/* default */.A .add ("X3DRenderObject", X3DRenderObject_default_));
;// ./src/x_ite/Execution/BindableStack.js


function BindableStack (executionContext, defaultNode)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .nodes = [ defaultNode ];
}

Object .assign (Object .setPrototypeOf (BindableStack .prototype, Base_X3DBaseNode .prototype),
{
   get ()
   {
      return this .nodes;
   },
   top ()
   {
      return this .nodes .at (-1);
   },
   pushOnTop (node)
   {
      if (node !== this .nodes [0])
      {
         this .nodes .at (-1) ._isBound = false;
         this .nodes .push (node);
      }

      // Don't do set_bind.
      node ._isBound  = true;
      node ._bindTime = this .getBrowser () .getCurrentTime ();

      this .addNodeEvent ();
   },
   update (layerNode, removedNodes, changedNodes)
   {
      if (!removedNodes .length && !changedNodes .length)
         return;

      // Save top node for later use.

      const
         fromNode  = this .top (),
         boundNode = this .nodes .at (-1);

      // Remove invisible nodes and unbind them if needed.

      for (const removedNode of removedNodes)
      {
         const index = this .nodes .indexOf (removedNode);

         if (index > 0)
            this .nodes .splice (index, 1);
      }

      // Unbind nodes with set_bind false and pop top node.

      if (boundNode !== this .nodes [0])
      {
         if (changedNodes .some (node => !node ._set_bind .getValue () && node === boundNode))
         {
            this .nodes .pop ();
         }
      }

      // Push nodes with set_bind true to top of stack.

      for (const bindNode of changedNodes)
      {
         if (!bindNode ._set_bind .getValue ())
            continue;

         const index = this .nodes .indexOf (bindNode);

         if (index > -1)
            this .nodes .splice (index, 1);

         this .nodes .push (bindNode);
      }

      // Bind top node if not bound.

      const top = this .nodes .at (-1);

      if (top !== boundNode)
      {
         // First unbind last bound node.

         boundNode ._isBound = false;

         // Now bind new top node.

         top ._isBound  = true;
         top ._bindTime = this .getBrowser () .getCurrentTime ();

         // Do transition.

         top .transitionStart (layerNode, fromNode);
      }
      else
      {
         // Do transition.

         if (changedNodes .includes (top))
            top .transitionStart (layerNode, fromNode);
      }

      this .addNodeEvent ();
   },
});

for (const key of Object .keys (BindableStack .prototype))
   Object .defineProperty (BindableStack .prototype, key, { enumerable: false });

Object .defineProperties (BindableStack,
{
   typeName:
   {
      value: "BindableStack",
      enumerable: true,
   },
});

const BindableStack_default_ = BindableStack;
;

/* harmony default export */ const Execution_BindableStack = (Namespace/* default */.A .add ("BindableStack", BindableStack_default_));
;// ./src/x_ite/Components/Core/X3DBindableNode.js




let modificationCount = 0;

function X3DBindableNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DBindableNode);

   this .modificationCount = 0;
}

Object .assign (Object .setPrototypeOf (X3DBindableNode .prototype, Core_X3DChildNode .prototype),
{
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this ._set_bind .addInterest ("set_bind__", this);
   },
   isCameraObject ()
   {
      return true;
   },
   getModificationCount ()
   {
      return this .modificationCount;
   },
   transitionStart ()
   { },
   set_bind__ ()
   {
      if (modificationCount === Number .MAX_SAFE_INTEGER)
         modificationCount = 0;

      this .modificationCount = ++ modificationCount;
   },
});

Object .defineProperties (X3DBindableNode, Core_X3DNode .getStaticProperties ("X3DBindableNode", "Core", 1));

Object .defineProperties (X3DBindableNode,
{
   getModificationCount:
   {
      value ()
      {
         return modificationCount;
      },
      enumerable: false,
   },
});

const X3DBindableNode_default_ = X3DBindableNode;
;

/* harmony default export */ const Core_X3DBindableNode = (Namespace/* default */.A .add ("X3DBindableNode", X3DBindableNode_default_));
;// ./src/x_ite/Execution/BindableList.js



function BindableList (executionContext, defaultNode)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .modificationCount = 0;
   this .nodes             = [ defaultNode ];
   this .collectedNodes    = [ defaultNode ];
   this .changedNodes      = [ ];
   this .removedNodes      = [ ];
}

Object .assign (Object .setPrototypeOf (BindableList .prototype, Base_X3DBaseNode .prototype),
{
   get ()
   {
      return this .nodes;
   },
   getBound (name)
   {
      const length = this .nodes .length;

      if (length === 1)
         return this .nodes [0]; // Return default viewpoint.

      const enableInlineBindables = false;

      if (name)
      {
         // Return first viewpoint with @name.

         for (let i = 1; i < length; ++ i)
         {
            const
               node  = this .nodes [i],
               scene = node .getExecutionContext () .getOuterNode () ?.getScene () ?? node .getScene ();

            if (!enableInlineBindables && scene .getExecutionContext ())
               continue;

            if (node .getName () == name)
               return node;
         }
      }

      // Return first bound viewpoint in scene.

      for (let i = 1; i < length; ++ i)
      {
         const
            node  = this .nodes [i],
            scene = node .getExecutionContext () .getOuterNode () ?.getScene () ?? node .getScene ();

         if (!enableInlineBindables && scene .getExecutionContext ())
            continue;

         if (node ._isBound .getValue ())
            return node;
      }

      // Return first viewpoint in scene.

      for (let i = 1; i < length; ++ i)
      {
         const
            node  = this .nodes [i],
            scene = node .getExecutionContext () .getOuterNode () ?.getScene () ?? node .getScene ();

         if (!enableInlineBindables && scene .getExecutionContext ())
            continue;

         return node;
      }

      return this .nodes [0]; // Return default viewpoint.
   },
   push (node)
   {
      return this .collectedNodes .push (node);
   },
   update (layerNode, stack)
   {
      const { collectedNodes, changedNodes, removedNodes } = this;

      for (const node of collectedNodes)
      {
         if (node .getModificationCount () > this .modificationCount)
            changedNodes .push (node);
      }

      if (!equals (collectedNodes, this .nodes))
      {
         // Unbind nodes not in current list (collectedNodes);

         for (const node of this .nodes)
         {
            if (!collectedNodes .includes (node))
               removedNodes .push (node);
         }

         // Swap nodes.

         const tmp = this .nodes;

         this .nodes          = collectedNodes;
         this .collectedNodes = tmp;

         this .addNodeEvent ();
      }

      // Clear collected nodes.

      this .collectedNodes .length = 1;

      // Update stack.

      stack .update (layerNode, removedNodes, changedNodes)

      changedNodes .length = 0;
      removedNodes .length = 0;

      // Advance modificationCount time.

      this .modificationCount = Core_X3DBindableNode .getModificationCount ();
   },
});

for (const key of Object .keys (BindableList .prototype))
   Object .defineProperty (BindableList .prototype, key, { enumerable: false });

// Compares two arrays.

function equals (lhs, rhs)
{
   const lhsLength = lhs .length;

   if (lhsLength !== rhs .length)
      return false;

   for (let i = 0; i < lhsLength; ++ i)
   {
      if (lhs [i] !== rhs [i])
         return false;
   }

   return true;
}

Object .defineProperties (BindableList,
{
   typeName:
   {
      value: "BindableList",
      enumerable: true,
   },
});

const BindableList_default_ = BindableList;
;

/* harmony default export */ const Execution_BindableList = (Namespace/* default */.A .add ("BindableList", BindableList_default_));
;// ./src/x_ite/Base/X3DCast.js



function X3DCast (type, node, innerNode = true)
{
   try
   {
      if (node instanceof x_ite_Fields .SFNode)
         node = node .getValue ();

      node = innerNode ? node ?.getInnerNode () : node ?.valueOf ();

      if (node instanceof Core_X3DNode)
      {
         if (node .getType () .includes (type))
            return node;
      }
   }
   catch
   { }

   return null;
}

const X3DCast_default_ = X3DCast;
;

/* harmony default export */ const Base_X3DCast = (Namespace/* default */.A .add ("X3DCast", X3DCast_default_));
;// ./src/x_ite/Components/Grouping/X3DGroupingNode.js








function X3DGroupingNode (executionContext)
{
   Core_X3DChildNode     .call (this, executionContext);
   Grouping_X3DBoundedObject .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DGroupingNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "rebuild", new x_ite_Fields .SFTime ());

   this .setBoundedObject (true);
   this .setPointingObject (true);
   this .setCollisionObject (true);
   this .setShadowObject (true);
   this .setVisibleObject (true);

   // Private properties

   this .allowedTypes              = new Set ();
   this .pointingDeviceSensorNodes = new Set ();
   this .pointingObjects           = new Set ();
   this .clipPlaneNodes            = new Set ();
   this .displayNodes              = new Set ();
   this .cameraObjects             = new Set ();
   this .pickableSensorNodes       = new Set ();
   this .pickableObjects           = new Set ();
   this .collisionObjects          = new Set ();
   this .shadowObjects             = new Set ();
   this .childNodes                = new Set ();
   this .visibleObjects            = new Set ();
   this .boundedObjects            = new Set ();
   this .sensors                   = [ ];
}

Object .assign (Object .setPrototypeOf (X3DGroupingNode .prototype, Core_X3DChildNode .prototype),
   Grouping_X3DBoundedObject .prototype,
{
   initialize ()
   {
      Core_X3DChildNode     .prototype .initialize .call (this);
      Grouping_X3DBoundedObject .prototype .initialize .call (this);

      this ._rebuild          .addInterest ("set_children__",        this);
      this ._transformSensors .addInterest ("set_pickableObjects__", this);

      this ._bboxSize       .addInterest ("set_boundedObjects__", this);
      this ._addChildren    .addInterest ("set_addChildren__",    this);
      this ._removeChildren .addInterest ("set_removeChildren__", this);
      this ._children       .addInterest ("requestRebuild",       this);

      this .set_children__ ();
   },
   addAllowedTypes (... types)
   {
      for (const type of types)
         this .allowedTypes .add (type);
   },
   getBBox (bbox, shadows)
   {
      if (this .isDefaultBBoxSize ())
         return this .getSubBBox (bbox, shadows);

      return bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
   },
   getSubBBox (bbox, shadows)
   {
      return Grouping_X3DBoundedObject .prototype .getBBox .call (this, this .boundedObjects, bbox, shadows);
   },
   getShapes (shapes, modelMatrix)
   {
      for (const visibleObject of this .visibleObjects)
         visibleObject .getShapes (shapes, modelMatrix);

      return shapes;
   },
   requestRebuild ()
   {
      this ._rebuild .addEvent ();
   },
   set_addChildren__ ()
   {
      if (this ._addChildren .length === 0)
         return;

      this ._addChildren .setTainted (true);

      const addChildren = new Set (this ._addChildren);

      for (const node of this ._children)
         addChildren .delete (node);

      this .addChildren (addChildren);

      if (!this ._children .isTainted ())
      {
         this ._children .removeInterest ("requestRebuild", this);
         this ._children .addInterest ("connectChildren", this);
      }

      for (const child of addChildren)
         this ._children .push (child);

      this ._addChildren .length = 0;
      this ._addChildren .setTainted (false);
   },
   set_removeChildren__ ()
   {
      if (this ._removeChildren .length === 0)
         return;

      this ._removeChildren .setTainted (true);

      if (this ._children .length > 0)
      {
         const removeChildren = new Set (this ._removeChildren);

         this .removeChildren (removeChildren);

         if (!this ._children .isTainted ())
         {
            this ._children .removeInterest ("requestRebuild", this);
            this ._children .addInterest ("connectChildren", this);
         }

         this ._children = this ._children .filter (child => !removeChildren .has (child));
      }

      this ._removeChildren .length = 0;
      this ._removeChildren .setTainted (false);
   },
   set_children__ ()
   {
      this .clearChildren ();
      this .addChildren (this ._children);
   },
   connectChildren ()
   {
      this ._children .removeInterest ("connectChildren", this);
      this ._children .addInterest ("requestRebuild", this);
   },
   clearChildren ()
   {
      for (const childNode of this .childNodes)
      {
         childNode ._isBoundedObject   .removeInterest ("requestRebuild", this);
         childNode ._isPointingObject  .removeInterest ("requestRebuild", this);
         childNode ._isCameraObject    .removeInterest ("requestRebuild", this);
         childNode ._isPickableObject  .removeInterest ("requestRebuild", this);
         childNode ._isCollisionObject .removeInterest ("requestRebuild", this);
         childNode ._isShadowObject    .removeInterest ("requestRebuild", this);
         childNode ._isVisibleObject   .removeInterest ("requestRebuild", this);

         if (Base_X3DCast (Base_X3DConstants .X3DBoundedObject, childNode))
         {
            childNode ._display     .removeInterest ("requestRebuild", this);
            childNode ._bboxDisplay .removeInterest ("requestRebuild", this);
         }
      }

      this .boundedObjects            .clear ();
      this .pointingDeviceSensorNodes .clear ();
      this .pointingObjects           .clear ();
      this .clipPlaneNodes            .clear ();
      this .displayNodes              .clear ();
      this .cameraObjects             .clear ();
      this .pickableSensorNodes       .clear ();
      this .pickableObjects           .clear ();
      this .collisionObjects          .clear ();
      this .shadowObjects             .clear ();
      this .childNodes                .clear ();
      this .visibleObjects            .clear ();
   },
   addChildren (children)
   {
      // Make sure that the order of children is preserved,
      // otherwise flickering of transparent objects may occur.

      for (const child of children)
         this .addChild (child);

      this .set_objects__ ();
   },
   addChild (child)
   {
      const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

      if (!childNode)
         return;

      const type = childNode .getType ();

      if (this .allowedTypes .size)
      {
         if (!type .some (Set .prototype .has, this .allowedTypes))
            return;
      }

      for (let t = type .length - 1; t >= 0; -- t)
      {
         switch (type [t])
         {
            case Base_X3DConstants .X3DPointingDeviceSensorNode:
            {
               this .pointingDeviceSensorNodes .add (childNode);
               continue;
            }
            case Base_X3DConstants .ClipPlane:
            {
               this .clipPlaneNodes .add (childNode);
               this .displayNodes   .add (childNode);
               continue;
            }
            case Base_X3DConstants .LocalFog:
            case Base_X3DConstants .X3DLightNode:
            {
               this .displayNodes .add (childNode);
               continue;
            }
            case Base_X3DConstants .TransformSensor:
            case Base_X3DConstants .X3DPickSensorNode:
            {
               if (childNode .isPickableObject ())
                  this .pickableSensorNodes .add (childNode);

               continue;
            }
            case Base_X3DConstants .X3DChildNode:
            {
               childNode ._isBoundedObject   .addInterest ("requestRebuild", this);
               childNode ._isPointingObject  .addInterest ("requestRebuild", this);
               childNode ._isCameraObject    .addInterest ("requestRebuild", this);
               childNode ._isPickableObject  .addInterest ("requestRebuild", this);
               childNode ._isCollisionObject .addInterest ("requestRebuild", this);
               childNode ._isShadowObject    .addInterest ("requestRebuild", this);
               childNode ._isVisibleObject   .addInterest ("requestRebuild", this);

               this .childNodes .add (childNode);

               if (childNode .isVisible ())
               {
                  if (childNode .isBoundedObject ())
                     this .boundedObjects .add (childNode);

                  if (childNode .isPointingObject ())
                     this .pointingObjects .add (childNode);

                  if (childNode .isCameraObject ())
                     this .cameraObjects .add (childNode);

                  if (childNode .isPickableObject () && !this .pickableSensorNodes .has (childNode))
                     this .pickableObjects .add (childNode);

                  if (childNode .isCollisionObject ())
                     this .collisionObjects .add (childNode);

                  if (childNode .isShadowObject ())
                     this .shadowObjects .add (childNode);

                  if (childNode .isVisibleObject ())
                     this .visibleObjects .add (childNode);
               }

               if (Base_X3DCast (Base_X3DConstants .X3DBoundedObject, childNode))
               {
                  childNode ._display     .addInterest ("requestRebuild", this);
                  childNode ._bboxDisplay .addInterest ("requestRebuild", this);

                  if (childNode .isBBoxVisible ())
                     this .visibleObjects .add (childNode .getBBoxNode ());
               }

               break;
            }
            default:
               continue;
         }

         break;
      }
   },
   removeChildren (children)
   {
      for (const child of children)
         this .removeChild (child);

      this .set_objects__ ();
   },
   removeChild (child)
   {
      const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

      if (!childNode)
         return;

      const type = childNode .getType ();

      for (let t = type .length - 1; t >= 0; -- t)
      {
         switch (type [t])
         {
            case Base_X3DConstants .X3DPointingDeviceSensorNode:
            {
               this .pointingDeviceSensorNodes .delete (childNode);
               continue;
            }
            case Base_X3DConstants .ClipPlane:
            {
               this .clipPlaneNodes .delete (childNode);
               this .displayNodes   .delete (childNode);
               continue;
            }
            case Base_X3DConstants .LocalFog:
            case Base_X3DConstants .X3DLightNode:
            {
               this .displayNodes .delete (childNode);
               continue;
            }
            case Base_X3DConstants .TransformSensor:
            case Base_X3DConstants .X3DPickSensorNode:
            {
               this .pickableSensorNodes .delete (childNode);
               continue;
            }
            case Base_X3DConstants .X3DChildNode:
            {
               childNode ._isBoundedObject   .removeInterest ("requestRebuild", this);
               childNode ._isPointingObject  .removeInterest ("requestRebuild", this);
               childNode ._isCameraObject    .removeInterest ("requestRebuild", this);
               childNode ._isPickableObject  .removeInterest ("requestRebuild", this);
               childNode ._isCollisionObject .removeInterest ("requestRebuild", this);
               childNode ._isShadowObject    .removeInterest ("requestRebuild", this);
               childNode ._isVisibleObject   .removeInterest ("requestRebuild", this);

               if (Base_X3DCast (Base_X3DConstants .X3DBoundedObject, childNode))
               {
                  childNode ._display     .removeInterest ("requestRebuild", this);
                  childNode ._bboxDisplay .removeInterest ("requestRebuild", this);
               }

               this .boundedObjects   .delete (childNode);
               this .pointingObjects  .delete (childNode);
               this .cameraObjects    .delete (childNode);
               this .pickableObjects  .delete (childNode);
               this .collisionObjects .delete (childNode);
               this .shadowObjects    .delete (childNode);
               this .childNodes       .delete (childNode);
               this .visibleObjects   .delete (childNode);
               break;
            }
            default:
               continue;
         }

         break;
      }
   },
   set_objects__ ()
   {
      this .set_boundedObjects__ ();
      this .set_pointingObjects__ ();
      this .set_cameraObjects__ ();
      this .set_pickableObjects__ ();
      this .set_collisionObjects__ ();
      this .set_shadowObjects__ ();
      this .set_visibleObjects__ ();
   },
   set_boundedObjects__ ()
   {
      this .setBoundedObject (this .boundedObjects .size || !this .isDefaultBBoxSize ());
   },
   set_pointingObjects__ ()
   {
      this .setPointingObject (this .pointingObjects .size);
   },
   set_cameraObjects__ ()
   {
      this .setCameraObject (this .cameraObjects .size);
   },
   set_pickableObjects__ ()
   {
      this .setPickableObject (this .getTransformSensors () .size || this .pickableSensorNodes .size || this .pickableObjects .size);
   },
   set_collisionObjects__ ()
   {
      this .setCollisionObject (this .collisionObjects .size);
   },
   set_shadowObjects__ ()
   {
      this .setShadowObject (this .shadowObjects .size);
   },
   set_visibleObjects__ ()
   {
      this .setVisibleObject (this .visibleObjects .size);
   },
   traverse (type, renderObject)
   {
      switch (type)
      {
         case Rendering_TraverseType .POINTER:
         {
            const { sensors, pointingDeviceSensorNodes, clipPlaneNodes, pointingObjects } = this;

            if (pointingDeviceSensorNodes .size)
            {
               for (const pointingDeviceSensorNode of pointingDeviceSensorNodes)
                  pointingDeviceSensorNode .push (renderObject, sensors);

               if (sensors .length)
                  renderObject .getSensors () .push (sensors);
            }

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .push (renderObject);

            for (const pointingObject of pointingObjects)
               pointingObject .traverse (type, renderObject);

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .pop (renderObject);

            if (sensors .length)
            {
               sensors .length = 0;

               renderObject .getSensors () .pop ();
            }

            return;
         }
         case Rendering_TraverseType .CAMERA:
         {
            for (const cameraObject of this .cameraObjects)
               cameraObject .traverse (type, renderObject);

            return;
         }
         case Rendering_TraverseType .PICKING:
         {
            if (this .getTransformSensors () .size)
            {
               const modelMatrix = renderObject .getModelViewMatrix () .get ();

               for (const transformSensorNode of this .getTransformSensors ())
                  transformSensorNode .collect (modelMatrix);
            }

            for (const pickableSensorNode of this .pickableSensorNodes)
               pickableSensorNode .traverse (type, renderObject);

            const
               browser          = this .getBrowser (),
               pickingHierarchy = browser .getPickingHierarchy ();

            pickingHierarchy .push (this);

            if (browser .getPickable () .at (-1))
            {
               for (const visibleObject of this .visibleObjects)
                  visibleObject .traverse (type, renderObject);
            }
            else
            {
               for (const pickableObject of this .pickableObjects)
                  pickableObject .traverse (type, renderObject);
            }

            pickingHierarchy .pop ();
            return;
         }
         case Rendering_TraverseType .COLLISION:
         {
            const { clipPlaneNodes, collisionObjects } = this;

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .push (renderObject);

            for (const collisionObject of collisionObjects)
               collisionObject .traverse (type, renderObject);

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .pop (renderObject);

            return;
         }
         case Rendering_TraverseType .DEPTH:
         {
            for (const visibleObject of this .visibleObjects)
               visibleObject .traverse (type, renderObject);

            return;
         }
         case Rendering_TraverseType .SHADOW:
         {
            // Nodes that are not visible do not cast shadows.

            const { clipPlaneNodes, shadowObjects } = this;

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .push (renderObject);

            for (const shadowObject of shadowObjects)
               shadowObject .traverse (type, renderObject);

            for (const clipPlaneNode of clipPlaneNodes)
               clipPlaneNode .pop (renderObject);

            return;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            const { displayNodes, visibleObjects } = this;

            for (const displayNode of displayNodes)
               displayNode .push (renderObject, this);

            for (const visibleObject of visibleObjects)
               visibleObject .traverse (type, renderObject);

            for (const displayNode of displayNodes)
               displayNode .pop (renderObject);

            return;
         }
      }
   },
   dispose ()
   {
      Grouping_X3DBoundedObject .prototype .dispose .call (this);
      Core_X3DChildNode     .prototype .dispose .call (this);
   },
});

Object .defineProperties (X3DGroupingNode, Core_X3DNode .getStaticProperties ("X3DGroupingNode", "Grouping", 1));

const X3DGroupingNode_default_ = X3DGroupingNode;
;

/* harmony default export */ const Grouping_X3DGroupingNode = (Namespace/* default */.A .add ("X3DGroupingNode", X3DGroupingNode_default_));
;// ./src/x_ite/Components/Grouping/Group.js







function Group (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Group);
}

Object .setPrototypeOf (Group .prototype, Grouping_X3DGroupingNode .prototype);

Object .defineProperties (Group,
{
   ... Core_X3DNode .getStaticProperties ("Group", "Grouping", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",       new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Group_default_ = Group;
;

/* harmony default export */ const Grouping_Group = (Namespace/* default */.A .add ("Group", Group_default_));
;// ./src/x_ite/Components/Navigation/NavigationInfo.js







function NavigationInfo (executionContext)
{
   Core_X3DBindableNode .call (this, executionContext);

   this .addType (Base_X3DConstants .NavigationInfo);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transitionStart",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "transitionActive", new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "availableViewers", new x_ite_Fields .MFString (),
                          Base_X3DConstants .outputOnly, "viewer",           new x_ite_Fields .SFString ());

   // Units

   this ._avatarSize      .setUnit ("length");
   this ._speed           .setUnit ("speed");
   this ._visibilityLimit .setUnit ("length");

   // Legacy

   // Actually the type field had this value, but we never knew this.
   // if (executionContext .getSpecificationVersion () == 2.0)
   //    this ._type = ["WALK", "ANY"]; // VRML2
}

Object .assign (Object .setPrototypeOf (NavigationInfo .prototype, Core_X3DBindableNode .prototype),
{
   initialize ()
   {
      Core_X3DBindableNode .prototype .initialize .call (this);

      this ._type               .addInterest ("set_type__",               this);
      this ._avatarSize         .addInterest ("set_avatarSize__",         this);
      this ._headlight          .addInterest ("set_headlight__",          this);
      this ._visibilityLimit    .addInterest ("set_visibilityLimit__",    this);
      this ._transitionStart    .addInterest ("set_transitionStart__",    this);
      this ._transitionComplete .addInterest ("set_transitionComplete__", this);
      this ._isBound            .addInterest ("set_isBound__",            this);

      this .set_type__ ();
      this .set_avatarSize__ ();
      this .set_headlight__ ();
      this .set_visibilityLimit__ ();
   },
   getViewer ()
   {
      return this ._viewer .getValue ();
   },
   getCollisionRadius ()
   {
      return this .collisionRadius;
   },
   getAvatarHeight ()
   {
      return this .avatarHeight;
   },
   getStepHeight ()
   {
      return this .stepHeight;
   },
   getNearValue ()
   {
      return this .nearValue;
   },
   getFarValue ()
   {
      return this .visibilityLimit;
   },
   getTransitionType: (() =>
   {
      const TransitionTypes = new Set ([
         "TELEPORT",
         "LINEAR",
         "ANIMATE",
      ]);

      return function ()
      {
         for (const value of this ._transitionType)
         {
            if (TransitionTypes .has (value))
               return value;
         }

         return "LINEAR";
      };
   })(),
   set_type__ ()
   {
      // Determine active viewer.

      this ._viewer = "EXAMINE";

      for (const string of this ._type)
      {
         switch (string)
         {
            case "EXAMINE":
            case "WALK":
            case "FLY":
            case "LOOKAT":
            case "PLANE":
            case "NONE":
               this ._viewer = string;
               break;
            case "EXPLORE":
               this ._viewer = "EXAMINE";
               break;
            case "PLANE_create3000.github.io":
            case "PLANE_create3000.de":
               this ._viewer = "PLANE";
               break;
            default:
               continue;
         }

         // Leave for loop.
         break;
      }

      // Determine available viewers.

      let
         examineViewer = false,
         walkViewer    = false,
         flyViewer     = false,
         planeViewer   = false,
         noneViewer    = false,
         lookAt        = false;

      if (!this ._type .length)
      {
         examineViewer = true;
         walkViewer    = true;
         flyViewer     = true;
         planeViewer   = true;
         noneViewer    = true;
         lookAt        = true;
      }
      else
      {
         for (const string of this ._type)
         {
            switch (string)
            {
               case "EXAMINE":
                  examineViewer = true;
                  continue;
               case "WALK":
                  walkViewer = true;
                  continue;
               case "FLY":
                  flyViewer = true;
                  continue;
               case "LOOKAT":
                  lookAt = true;
                  continue;
               case "PLANE":
               case "PLANE_create3000.github.io":
               case "PLANE_create3000.de":
                  planeViewer = true;
                  continue;
               case "NONE":
                  noneViewer = true;
                  continue;
               case "ANY":
                  examineViewer = true;
                  walkViewer    = true;
                  flyViewer     = true;
                  planeViewer   = true;
                  noneViewer    = true;
                  lookAt        = true;
                  break;
               default:
                  // Any string leads to:
                  examineViewer = true;
                  continue;
            }

            break;
         }
      }

      this ._availableViewers .length = 0;

      if (examineViewer)
         this ._availableViewers .push ("EXAMINE");

      if (walkViewer)
         this ._availableViewers .push ("WALK");

      if (flyViewer)
         this ._availableViewers .push ("FLY");

      if (planeViewer)
         this ._availableViewers .push ("PLANE");

      if (lookAt)
         this ._availableViewers .push ("LOOKAT");

      if (noneViewer)
         this ._availableViewers .push ("NONE");
   },
   set_avatarSize__ ()
   {
      const collisionRadius = Math .max (this ._avatarSize [0] ?? 0.25, 0);

      this .collisionRadius = collisionRadius;
      this .avatarHeight    = Math .max (this ._avatarSize [1] ?? 1.6, 0);
      this .stepHeight      = Math .max (this ._avatarSize [2] ?? 0.75, 0);
      this .nearValue       = collisionRadius === 0 ? 0.125 : collisionRadius / 2;
   },
   set_headlight__ ()
   {
      if (this ._headlight .getValue ())
         delete this .enable;
      else
         this .enable = Function .prototype;
   },
   set_visibilityLimit__ ()
   {
      this .visibilityLimit = Math .max (this ._visibilityLimit .getValue (), 0);
   },
   set_transitionStart__ ()
   {
      if (!this ._transitionActive .getValue ())
         this ._transitionActive = true;
   },
   set_transitionComplete__ ()
   {
      if (this ._transitionActive .getValue ())
         this ._transitionActive = false;
   },
   set_isBound__ ()
   {
      if (this ._isBound .getValue ())
         return;

      if (this ._transitionActive .getValue ())
         this ._transitionActive = false;
   },
   enable (type, renderObject)
   {
      if (!this ._headlight .getValue ())
         return;

      const headlight = this .getBrowser () .getHeadlight ();

      renderObject .getGlobalLights ()     .push (headlight);
      renderObject .getGlobalLightsKeys () .push (headlight .lightNode .getLightKey ());
   },
   traverse (type, renderObject)
   {
      renderObject .getLayer () .getNavigationInfos () .push (this);
   }
});

Object .defineProperties (NavigationInfo,
{
   ... Core_X3DNode .getStaticProperties ("NavigationInfo", "Navigation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "type",               new x_ite_Fields .MFString ("EXAMINE", "ANY")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "avatarSize",         new x_ite_Fields .MFFloat (0.25, 1.6, 0.75)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "speed",              new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "headlight",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visibilityLimit",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transitionType",     new x_ite_Fields .MFString ("LINEAR")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transitionTime",     new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "transitionComplete", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",           new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const NavigationInfo_default_ = NavigationInfo;
;

/* harmony default export */ const Navigation_NavigationInfo = (Namespace/* default */.A .add ("NavigationInfo", NavigationInfo_default_));
;// ./src/standard/Utility/ObjectCache.js
function ObjectCache (Type)
{
   return Object .assign ([ ],
   {
      top: 0,
      pop ()
      {
         if (this .top !== 0)
            return this [-- this .top];

         return new Type ();
      },
      push (object)
      {
         this [this .top ++] = object;
      },
      clear ()
      {
         this .length = 0;
         this .top    = 0;
      },
   });
}

const ObjectCache_default_ = ObjectCache;
;

/* harmony default export */ const Utility_ObjectCache = (Namespace/* default */.A .add ("ObjectCache", ObjectCache_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/X3DFogObject.js





const Fogs = Utility_ObjectCache (FogContainer);

function FogContainer ()
{
   this .fogMatrix = new Float32Array (9);
}

Object .assign (FogContainer .prototype,
{
   set (fogNode, modelViewMatrix)
   {
      this .fogNode = fogNode;

      this .fogMatrix .set (modelViewMatrix .submatrix .inverse ());
   },
   getFogType ()
   {
      return this .fogNode .getFogType ();
   },
   setShaderUniforms (gl, shaderObject)
   {
      if (shaderObject .hasFog (this))
         return;

      const fogNode = this .fogNode;

      gl .uniform3fv       (shaderObject .x3d_FogColor,           fogNode .colorArray);
      gl .uniform1f        (shaderObject .x3d_FogVisibilityStart, fogNode .visibilityStart);
      gl .uniform1f        (shaderObject .x3d_FogVisibilityRange, fogNode .visibilityRange);
      gl .uniformMatrix3fv (shaderObject .x3d_FogMatrix, false,   this .fogMatrix);
   },
   dispose ()
   {
      Fogs .push (this);
   },
});

function X3DFogObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DFogObject);

   this .addChildObjects (Base_X3DConstants .inputOutput, "hidden", new x_ite_Fields .SFBool ());

   // Units

   this ._visibilityStart .setUnit ("length");
   this ._visibilityRange .setUnit ("length");

   // Private properties

   this .colorArray = new Float32Array (3);
}

Object .assign (X3DFogObject .prototype,
{
   initialize ()
   {
      this ._hidden          .addInterest ("set_fogType__",         this);
      this ._fogType         .addInterest ("set_fogType__",         this);
      this ._color           .addInterest ("set_color__",           this);
      this ._visibilityStart .addInterest ("set_visibilityRange__", this);
      this ._visibilityRange .addInterest ("set_visibilityRange__", this);

      this .set_color__ ();
      this .set_visibilityRange__ ();
   },
   isHidden ()
   {
      return this ._hidden .getValue ();
   },
   setHidden (value)
   {
      if (value === this ._hidden .getValue ())
         return;

      this ._hidden = value;
   },
   getFogType ()
   {
      return this .fogType;
   },
   getFogs ()
   {
      return Fogs;
   },
   set_fogType__: (() =>
   {
      const fogTypes = new Map ([
         ["LINEAR",      1],
         ["EXPONENTIAL", 2],
      ]);

      return function ()
      {
         if (this ._hidden .getValue () || (this .visibilityRange === 0 && this .visibilityStart === 0))
            this .fogType = 0;
         else
            this .fogType = fogTypes .get (this ._fogType .getValue ()) || 1;
      };
   })(),
   set_color__ ()
   {
      this .colorArray .set (this ._color .getValue ());
   },
   set_visibilityRange__ ()
   {
      const
         visibilityStart = Math .max (this ._visibilityStart .getValue (), 0),
         visibilityRange = Math .max (this ._visibilityRange .getValue (), 0);

      this .visibilityStart = Math .min (visibilityStart, visibilityRange);
      this .visibilityRange = visibilityRange;

      this .set_fogType__ ();
   },
   dispose ()
   { },
});

Object .defineProperties (X3DFogObject, Core_X3DNode .getStaticProperties ("X3DFogObject", "EnvironmentalEffects", 1));

const X3DFogObject_default_ = X3DFogObject;
;

/* harmony default export */ const EnvironmentalEffects_X3DFogObject = (Namespace/* default */.A .add ("X3DFogObject", X3DFogObject_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/Fog.js









function Fog (executionContext)
{
   Core_X3DBindableNode .call (this, executionContext);
   EnvironmentalEffects_X3DFogObject    .call (this, executionContext);

   this .addType (Base_X3DConstants .Fog);

   this .modelMatrix = new Numbers_Matrix4 ();
}

Object .assign (Object .setPrototypeOf (Fog .prototype, Core_X3DBindableNode .prototype),
   EnvironmentalEffects_X3DFogObject .prototype,
{
   initialize ()
   {
      Core_X3DBindableNode .prototype .initialize .call (this);
      EnvironmentalEffects_X3DFogObject    .prototype .initialize .call (this);
   },
   getModelMatrix ()
   {
      return this .modelMatrix;
   },
   traverse (type, renderObject)
   {
      renderObject .getLayer () .getFogs () .push (this);

      this .modelMatrix .assign (renderObject .getModelViewMatrix () .get ());
   },
   dispose ()
   {
      EnvironmentalEffects_X3DFogObject    .prototype .dispose .call (this);
      Core_X3DBindableNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (Fog,
{
   ... Core_X3DNode .getStaticProperties ("Fog", "EnvironmentalEffects", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",        new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fogType",         new x_ite_Fields .SFString ("LINEAR")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",           new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visibilityStart", new x_ite_Fields .SFFloat ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visibilityRange", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",        new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const Fog_default_ = Fog;
;

/* harmony default export */ const EnvironmentalEffects_Fog = (Namespace/* default */.A .add ("Fog", Fog_default_));
;// ./src/x_ite/Rendering/VertexArray.js
function VertexArray (gl)
{
   this .gl           = gl;
   this .vertexArrays = new Map ();
}

Object .assign (VertexArray .prototype,
{
   update (value = true)
   {
      if (value)
         this .dispose ();

      return this;
   },
   enable (program)
   {
      const { gl, vertexArrays } = this;

      const vertexArray = vertexArrays .get (program);

      if (vertexArray)
      {
         gl .bindVertexArray (vertexArray);

         return false;
      }
      else
      {
         // Memory leak prevention when shaders are reloaded. There should normally be no more than maybe 10 VAOs, except when shaders are often reloaded.
         if (vertexArrays .size > 100)
            this .dispose ();

         const vertexArray = gl .createVertexArray ();

         vertexArrays .set (program, vertexArray)

         gl .bindVertexArray (vertexArray);

         // console .log ("rebuild vao");

         return true; // Rebuild
      }
   },
   dispose ()
   {
      const { gl, vertexArrays } = this;

      for (const vertexArray of vertexArrays .values ())
         gl .deleteVertexArray (vertexArray);

      vertexArrays .clear ();
   },
});

const VertexArray_default_ = VertexArray;
;

/* harmony default export */ const Rendering_VertexArray = (Namespace/* default */.A .add ("VertexArray", VertexArray_default_));
;// ./src/x_ite/Browser/Rendering/MikkTSpace.js


// See: https://github.com/donmccurdy/mikktspace-wasm

const MikkTSpace_default_ = new class MikkTSpace
{
   #promise;

   async initialize ()
   {
      return this .#promise = this .#promise ?? new Promise (async resolve =>
      {
         const imports =
         {
            wbg:
            {
               __wbindgen_string_new: (arg0, arg1) =>
               {
                  const ret = this .#getStringFromWasm0 (arg0, arg1);

                  return this .#addHeapObject (ret);
               },
               __wbindgen_rethrow: (arg0) =>
               {
                  throw this .#takeObject (arg0);
               },
            },
         };

         const input = await fetch (Networking_URLs .getLibraryURL ("mikktspace_bg.wasm"));

         const { instance } = await this .#load (input, imports);

         this .#wasm = instance .exports;

         resolve ();
      });
   }

   isInitialized ()
   {
      return !! this .#wasm;
   }

   async #load (response, imports)
   {
      if (typeof WebAssembly .instantiateStreaming === "function")
      {
         try
         {
            return await WebAssembly .instantiateStreaming (response, imports);
         }
         catch (error)
         {
            if (response .headers .get ("Content-Type") !== "application/wasm")
            {
               // console .warn ("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", error);
            }
            else
            {
               throw error;
            }
         }
      }

      const bytes = await response .arrayBuffer ();

      return await WebAssembly .instantiate (bytes, imports);
   }

   /**
   * Generates vertex tangents for the given position/normal/texcoord attributes.
   * @param {Float32Array} position vec3
   * @param {Float32Array} normal vec3
   * @param {Float32Array} texcoord vec2
   * @returns {Float32Array} vec4
   */
   generateTangents (position, normal, texcoord)
   {
      try
      {
         const
            retptr = this .#wasm .__wbindgen_add_to_stack_pointer (-16),
            ptr0   = this .#passArrayF32ToWasm0 (position, this .#wasm .__wbindgen_malloc),
            len0   = this .#WASM_VECTOR_LEN,
            ptr1   = this .#passArrayF32ToWasm0 (normal, this .#wasm .__wbindgen_malloc),
            len1   = this .#WASM_VECTOR_LEN,
            ptr2   = this .#passArrayF32ToWasm0 (texcoord, this .#wasm .__wbindgen_malloc),
            len2   = this .#WASM_VECTOR_LEN;

         this .#wasm .generateTangents (retptr, ptr0, len0, ptr1, len1, ptr2, len2);

         const
            r0 = this .#getInt32Memory0 () [retptr / 4 + 0],
            r1 = this .#getInt32Memory0 () [retptr / 4 + 1],
            v3 = this .#getArrayF32FromWasm0 (r0, r1) .slice ();

         this .#wasm .__wbindgen_free (r0, r1 * 4);

         return v3;
      }
      finally
      {
         this .#wasm .__wbindgen_add_to_stack_pointer (16);
      }
   }

   #wasm;
   #textDecoder = new TextDecoder ("utf-8", { ignoreBOM: true, fatal: true });
   #uint8Memory0;

   #getUint8Memory0 ()
   {
      if (this .#uint8Memory0 ?.buffer !== this .#wasm .memory .buffer)
         this .#uint8Memory0 = new Uint8Array (this .#wasm .memory .buffer);

      return this .#uint8Memory0;
   }

   #getStringFromWasm0 (ptr, len)
   {
      return this .#textDecoder .decode (this .#getUint8Memory0 () .subarray (ptr, ptr + len));
   }

   #heap = Array .from ({ length: 32 }) .concat ([undefined, null, true, false]);
   #heap_next = this .#heap .length;

   #addHeapObject (obj)
   {
      if (this .#heap_next === this .#heap .length)
         this .#heap .push (this .#heap .length + 1);

      const i = this .#heap_next;

      this .#heap_next = this .#heap [i];
      this .#heap [i]  = obj;

      return i;
   }

   #getObject (i)
   {
      return this .#heap [i];
   }

   #dropObject (i)
   {
      if (i < 36)
         return;

      this .#heap [i] = this .#heap_next;

      this .#heap_next = i;
   }

   #takeObject (i)
   {
      const ret = this .#getObject (i);

      this .#dropObject (i);

      return ret;
   }

   #float32Memory0;

   #getFloat32Memory0 ()
   {
      if (this .#float32Memory0 ?.buffer !== this .#wasm .memory .buffer)
         this .#float32Memory0 = new Float32Array (this .#wasm .memory .buffer);

      return this .#float32Memory0;
   }

   #WASM_VECTOR_LEN = 0;

   #passArrayF32ToWasm0 (arg, malloc)
   {
      const ptr = malloc (arg .length * 4);

      this .#getFloat32Memory0 () .set (arg, ptr / 4);

      this .#WASM_VECTOR_LEN = arg .length;

      return ptr;
   }

   #int32Memory0;

   #getInt32Memory0 ()
   {
      if (this .#int32Memory0 ?.buffer !== this .#wasm .memory .buffer)
         this .#int32Memory0 = new Int32Array (this .#wasm .memory .buffer);

      return this .#int32Memory0;
   }

   #getArrayF32FromWasm0 (ptr, len)
   {
      return this .#getFloat32Memory0 () .subarray (ptr / 4, ptr / 4 + len);
   }
};
;

/* harmony default export */ const MikkTSpace = (Namespace/* default */.A .add ("MikkTSpace", MikkTSpace_default_));
;// ./src/x_ite/Components/Rendering/X3DGeometryNode.js













// Box normals for bbox / line intersection.
const boxNormals = [
   Numbers_Vector3 .Z_AXIS,          // front
   Numbers_Vector3 .NEGATIVE_Z_AXIS, // back
   Numbers_Vector3 .Y_AXIS,          // top
   Numbers_Vector3 .NEGATIVE_Y_AXIS, // bottom
   Numbers_Vector3 .X_AXIS,          // right
   // left: We do not need to test for left.
];

function X3DGeometryNode (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DGeometryNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "bbox_changed", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "rebuild",      new x_ite_Fields .SFTime ());

   // Private properties

   this .min                      = new Numbers_Vector3 ();
   this .max                      = new Numbers_Vector3 ();
   this .bbox                     = Geometry_Box3 .fromExtents (this .min, this .max);
   this .solid                    = true;
   this .geometryType             = 3;
   this .colorMaterial            = false;
   this .attribNodes              = [ ];
   this .attribArrays             = [ ];
   this .textureCoordinateMapping = new Map ();
   this .multiTexCoords           = [ ];
   this .coordIndices             = X3DGeometryNode .createArray (Uint32Array);
   this .texCoords                = X3DGeometryNode .createArray ();
   this .fogDepths                = X3DGeometryNode .createArray ();
   this .colors                   = X3DGeometryNode .createArray ();
   this .tangents                 = X3DGeometryNode .createArray ();
   this .normals                  = X3DGeometryNode .createArray ();
   this .vertices                 = X3DGeometryNode .createArray ();
   this .hasFogCoords             = false;
   this .hasNormals               = false;
   this .geometryKey              = "";
   this .vertexCount              = 0;
   this .planes                   = Array .from ({ length: 5 }, () => new Geometry_Plane3 ()); // For LinePickSensor
}

class GeometryArray
{
   static create (Type = Float32Array)
   {
      let typedArray = new Type ();

      return Object .assign ([ ],
      {
         assign (value)
         {
            const length = value .length;

            this .length = length;

            for (let i = 0; i < length; ++ i)
               this [i] = value [i];
         },
         getValue ()
         {
            return typedArray;
         },
         shrinkToFit ()
         {
            if (this .length === typedArray .length)
               typedArray .set (this);
            else
               typedArray = new Type (this);

            return typedArray;
         },
      });
   }
}

Object .defineProperty (X3DGeometryNode, "createArray",
{
   // Function to select ether Array or MFFloat for color/normal/vertex arrays.
   // Array version runs faster, see BeyondGermany and TreasureIsland.
   value (Type)
   {
      // return new Fields .MFFloat ();

      return GeometryArray .create (Type);
   },
})

Object .assign (Object .setPrototypeOf (X3DGeometryNode .prototype, Core_X3DNode .prototype),
{
   setup ()
   {
      Core_X3DNode .prototype .setup .call (this);

      this .rebuild ();
   },
   initialize ()
   {
      Core_X3DNode .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         gl      = browser .getContext ();

      this .getLive () .addInterest ("set_live__", this);

      this .addInterest ("requestRebuild", this);
      this ._rebuild .addInterest ("rebuild", this);

      this .coordIndexBuffer      = gl .createBuffer ();
      this .attribBuffers         = [ ];
      this .textureCoordinateNode = browser .getDefaultTextureCoordinate ();
      this .texCoordBuffers       = Array .from ({ length: browser .getMaxTexCoords () }, () => gl .createBuffer ());
      this .fogDepthBuffer        = gl .createBuffer ();
      this .colorBuffer           = gl .createBuffer ();
      this .tangentBuffer         = gl .createBuffer ();
      this .normalBuffer          = gl .createBuffer ();
      this .vertexBuffer          = gl .createBuffer ();
      this .vertexArrayObject     = new Rendering_VertexArray (gl);

      this .setCCW (true);
      this .set_live__ ();
   },
   getGeometryType ()
   {
      return this .geometryType;
   },
   setGeometryType (value)
   {
      this .geometryType = value;
   },
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
   getBBox ()
   {
      // With screen matrix applied.
      return this .bbox;
   },
   setBBox (bbox)
   {
      if (bbox .equals (this .bbox))
         return;

      const { min, max } = this;

      bbox .getExtents (min, max);

      this .bbox .assign (bbox);
      this .planes .forEach ((plane, i) => plane .set (i % 2 ? min : max, boxNormals [i]));

      this ._bbox_changed .addEvent ();

      this .getExecutionContext () ._bbox_changed = Date .now () / 1000;
   },
   getMin ()
   {
      // With screen matrix applied.
      return this .min;
   },
   getMax ()
   {
      // With screen matrix applied.
      return this .max;
   },
   getMatrix ()
   {
      return Numbers_Matrix4 .IDENTITY;
   },
   isSolid ()
   {
      return this .solid;
   },
   setSolid (value)
   {
      this .solid = value;
   },
   getCCW ()
   {
      const gl = this .getBrowser () .getContext ();

      return this .frontFace ===  gl .CCW;
   },
   setCCW (value)
   {
      const gl = this .getBrowser () .getContext ();

      this .frontFace = value ? gl .CCW : gl .CW;
      this .backFace  = value ? gl .CW  : gl .CCW;
   },
   getCoordIndices ()
   {
      return this .coordIndices;
   },
   getAttrib ()
   {
      return this .attribNodes;
   },
   getAttribs ()
   {
      return this .attribArrays;
   },
   getAttribBuffers ()
   {
      return this .attribBuffers;
   },
   getFogDepths ()
   {
      return this .fogDepths;
   },
   getColors ()
   {
      return this .colors;
   },
   getMultiTexCoords ()
   {
      return this .multiTexCoords;
   },
   getTexCoords ()
   {
      return this .texCoords;
   },
   getTextureCoordinate ()
   {
      return this .textureCoordinateNode;
   },
   setTextureCoordinate (value)
   {
      this .textureCoordinateNode .removeInterest ("updateTextureCoordinateMapping", this);

      this .textureCoordinateNode = value ?? this .getBrowser () .getDefaultTextureCoordinate ();

      this .textureCoordinateNode .addInterest ("updateTextureCoordinateMapping", this);

      this .updateTextureCoordinateMapping ();
   },
   getTextureCoordinateMapping ()
   {
      return this .textureCoordinateMapping;
   },
   updateTextureCoordinateMapping ()
   {
      this .textureCoordinateMapping .clear ();

      this .textureCoordinateNode .getTextureCoordinateMapping (this .textureCoordinateMapping);
   },
   getTangents ()
   {
      return this .tangents;
   },
   getNormals ()
   {
      return this .normals;
   },
   getVertices ()
   {
      return this .vertices;
   },
   updateVertexArrays ()
   {
      this .vertexArrayObject .update ();

      this .updateInstances = true;
   },
   generateTexCoords ()
   {
      const texCoords = this .texCoords;

      if (texCoords .length === 0)
      {
         const
            p         = this .getTexCoordParams (),
            min       = p .min,
            Sindex    = p .Sindex,
            Tindex    = p .Tindex,
            Ssize     = p .Ssize,
            S         = min [Sindex],
            T         = min [Tindex],
            vertices  = this .vertices .getValue (),
            length    = vertices .length;

         for (let i = 0; i < length; i += 4)
         {
            texCoords .push ((vertices [i + Sindex] - S) / Ssize,
                             (vertices [i + Tindex] - T) / Ssize,
                             0,
                             1);
         }

         texCoords .shrinkToFit ();
      }

      this .getMultiTexCoords () .push (texCoords);
   },
   getTexCoordParams: (() =>
   {
      const texCoordParams = { min: new Numbers_Vector3 (), Ssize: 0, Sindex: 0, Tindex: 0 };

      return function ()
      {
         const
            bbox  = this .getBBox (),
            size  = bbox .size,
            Xsize = size .x,
            Ysize = size .y,
            Zsize = size .z;

         texCoordParams .min .assign (bbox .center) .subtract (size .divide (2));

         if ((Xsize >= Ysize) && (Xsize >= Zsize))
         {
            // X size largest
            texCoordParams .Ssize  = Xsize;
            texCoordParams .Sindex = 0;

            if (Ysize >= Zsize)
               texCoordParams .Tindex = 1;
            else
               texCoordParams .Tindex = 2;
         }
         else if ((Ysize >= Xsize) && (Ysize >= Zsize))
         {
            // Y size largest
            texCoordParams .Ssize  = Ysize;
            texCoordParams .Sindex = 1;

            if (Xsize >= Zsize)
               texCoordParams .Tindex = 0;
            else
               texCoordParams .Tindex = 2;
         }
         else
         {
            // Z is the largest
            texCoordParams .Ssize  = Zsize;
            texCoordParams .Sindex = 2;

            if (Xsize >= Ysize)
               texCoordParams .Tindex = 0;
            else
               texCoordParams .Tindex = 1;
         }

         return texCoordParams;
      };
   })(),
   generateTangents ()
   {
      try
      {
         if (this .geometryType < 2)
            return;

         if (!this .vertices .length)
            return;

         if (!MikkTSpace .isInitialized ())
         {
            return void (MikkTSpace .initialize () .then (() =>
            {
               this .generateTangents ();
               this .transfer ();
               this .updateGeometryKey ();
               this .updateRenderFunctions ();
               this .getBrowser () .addBrowserEvent ();
            }));
         }

         const
            vertices  = this .vertices .getValue () .filter ((v, i) => i % 4 < 3),
            normals   = this .normals .getValue (),
            texCoords = this .multiTexCoords [0] .getValue () .filter ((v, i) => i % 4 < 2),
            tangents  = MikkTSpace .generateTangents (vertices, normals, texCoords),
            length    = tangents .length;

         // Convert coordinate system handedness to respect output format of MikkTSpace.
         for (let i = 3; i < length; i += 4)
            tangents [i] = -tangents [i]; // Flip w-channel.

         this .tangents .assign (tangents);
         this .tangents .shrinkToFit ();
      }
      catch (error)
      {
         if (DEVELOPMENT)
            console .error (error);
      }
   },
   refineNormals (normalIndex, normals, creaseAngle)
   {
      if (creaseAngle <= 0)
         return normals;

      const
         cosCreaseAngle = Math .cos (Math_Algorithm .clamp (creaseAngle, 0, Math .PI)),
         refinedNormals = [ ];

      for (const vertex of normalIndex .values ())
      {
         for (const p of vertex)
         {
            const
               P = normals [p],
               N = new Numbers_Vector3 ();

            for (const q of vertex)
            {
               const Q = normals [q];

               if (Q .dot (P) > cosCreaseAngle)
                  N .add (Q);
            }

            refinedNormals [p] = N .normalize ();
         }
      }

      return refinedNormals;
   },
   set_live__ ()
   {
      // Is overloaded by primitives with option nodes.
   },
   connectOptions (options)
   {
      const
         browser      = this .getBrowser (),
         alwaysUpdate = this .isLive () && browser .getBrowserOption ("AlwaysUpdateGeometries");

      if (this .getLive () .getValue () || alwaysUpdate)
      {
         options .addInterest ("requestRebuild", this);

         if (options .getModificationTime () >= this ._rebuild .getValue ())
            this .requestRebuild ();
      }
      else
      {
         options .removeInterest ("requestRebuild", this);
      }
   },
   requestRebuild ()
   {
      this ._rebuild = Date .now () / 1000;
   },
   rebuild ()
   {
      this .clear ();
      this .build ();

      // Fill with zero data if not provided.
      this .coordIndices .length ||= this .vertices .length / 4;

      // Shrink arrays before transferring them to graphics card.

      for (const attribArray of this .attribArrays)
         attribArray .shrinkToFit ();

      for (const multiTexCoord of this .multiTexCoords)
         multiTexCoord .shrinkToFit ();

      this .coordIndices .shrinkToFit ();
      this .fogDepths    .shrinkToFit ();
      this .colors       .shrinkToFit ();
      this .tangents     .shrinkToFit ();
      this .normals      .shrinkToFit ();
      this .vertices     .shrinkToFit ();

      this .updateBBox ();

      // Generate texCoord if needed.

      if (!this .multiTexCoords .length)
         this .generateTexCoords ();

      // Generate tangents if needed.

      if (!this .tangents .length)
         this .generateTangents ();

      // Transfer arrays and update.

      this .transfer ();
      this .updateGeometryKey ();
      this .updateRenderFunctions ();
   },
   clear ()
   {
      // BBox

      this .min .set (Number .POSITIVE_INFINITY);
      this .max .set (Number .NEGATIVE_INFINITY);

      // Create attribArray arrays.
      {
         const attribArrays = this .attribArrays;

         for (const attribArray of attribArrays)
            attribArray .length = 0;

         const length = this .attribNodes .length;

         for (let a = attribArrays .length; a < length; ++ a)
            attribArrays [a] = X3DGeometryNode .createArray ();

         attribArrays .length = length;
      }

      // Buffer

      this .coordIndices   .length = 0;
      this .fogDepths      .length = 0;
      this .colors         .length = 0;
      this .multiTexCoords .length = 0;
      this .texCoords      .length = 0;
      this .tangents       .length = 0;
      this .normals        .length = 0;
      this .vertices       .length = 0;
   },
   updateBBox ()
   {
      // Determine bbox.

      const
         vertices     = this .vertices .getValue (),
         { min, max } = this;

      if (vertices .length)
      {
         if (min .x === Number .POSITIVE_INFINITY)
            this .bbox .setArray (vertices, 4) .getExtents (min, max);
         else
            this .bbox .setExtents (min, max);
      }
      else
      {
         this .bbox .setExtents (min .set (0), max .set (0));
      }

      for (let i = 0; i < 5; ++ i)
         this .planes [i] .set (i % 2 ? min : max, boxNormals [i]);

      this ._bbox_changed .addEvent ();

      this .getExecutionContext () ._bbox_changed = Date .now () / 1000;
   },
   transfer ()
   {
      const gl = this .getBrowser () .getContext ();

      // Transfer coord indices.

      gl .bindBuffer (gl .ARRAY_BUFFER, this .coordIndexBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .coordIndices .getValue (), gl .DYNAMIC_DRAW);

      // Transfer attribArrays.

      for (let i = this .attribBuffers .length, length = this .attribArrays .length; i < length; ++ i)
         this .attribBuffers .push (gl .createBuffer ());

      for (let i = 0, length = this .attribArrays .length; i < length; ++ i)
      {
         gl .bindBuffer (gl .ARRAY_BUFFER, this .attribBuffers [i]);
         gl .bufferData (gl .ARRAY_BUFFER, this .attribArrays [i] .getValue (), gl .DYNAMIC_DRAW);
      }

      // Transfer fog depths.

      const lastHasFogCoords = this .hasFogCoords;

      gl .bindBuffer (gl .ARRAY_BUFFER, this .fogDepthBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .fogDepths .getValue (), gl .DYNAMIC_DRAW);

      this .hasFogCoords = !! this .fogDepths .length;

      if (this .hasFogCoords !== lastHasFogCoords)
         this .updateVertexArrays ();

      // Transfer colors.

      const lastColorMaterial = this .colorMaterial;

      gl .bindBuffer (gl .ARRAY_BUFFER, this .colorBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .colors .getValue (), gl .DYNAMIC_DRAW);

      this .colorMaterial = !! this .colors .length;

      if (this .colorMaterial !== lastColorMaterial)
         this .updateVertexArrays ();

      // Transfer multiTexCoords.

      for (let i = 0, length = this .multiTexCoords .length; i < length; ++ i)
      {
         gl .bindBuffer (gl .ARRAY_BUFFER, this .texCoordBuffers [i]);
         gl .bufferData (gl .ARRAY_BUFFER, this .multiTexCoords [i] .getValue (), gl .DYNAMIC_DRAW);
      }

      // Transfer tangents.

      const lastHasTangents = this .hasTangents;

      gl .bindBuffer (gl .ARRAY_BUFFER, this .tangentBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .tangents .getValue (), gl .DYNAMIC_DRAW);

      this .hasTangents = !! this .tangents .length;

      if (this .hasTangents !== lastHasTangents)
         this .updateVertexArrays ();

      // Transfer normals or flat normals.

      const lastHasNormals = this .hasNormals;

      gl .bindBuffer (gl .ARRAY_BUFFER, this .normalBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .normals .getValue (), gl .DYNAMIC_DRAW);

      this .hasNormals = !! this .normals .length;

      if (this .hasNormals !== lastHasNormals)
         this .updateVertexArrays ();

      // Transfer vertices.

      gl .bindBuffer (gl .ARRAY_BUFFER, this .vertexBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .vertices .getValue (), gl .DYNAMIC_DRAW);

      this .vertexCount = this .vertices .length / 4;
   },
   updateGeometryKey ()
   {
      let key = "";

      key += this .geometryType;
      key += this .hasFogCoords  ? 1 : 0;
      key += this .colorMaterial ? 1 : 0;
      key += this .hasTangents   ? 1 : 0;
      key += this .hasNormals    ? 1 : 0;

      this .geometryKey = key;
   },
   updateRenderFunctions ()
   {
      if (this .vertexCount)
      {
         // Use default render functions.

         this .setBase (this .base);
      }
      else
      {
         // Use no render function.

         this .displaySimple          = Function .prototype;
         this .display                = Function .prototype;
         this .displaySimpleInstanced = Function .prototype;
         this .displayInstanced       = Function .prototype;
      }
   },
   setBase: (() =>
   {
      // Actually all functions that are overloaded must be listed here.

      const functions = [
         "intersectsLine",
         "updateVertexArrays",
         "updateLengthSoFar",
         "generateTexCoords",
         "displaySimple",
         "displaySimpleThick",
         "displaySimpleInstanced",
         "displaySimpleInstancedThick",
         "display",
         "displayThick",
         "displayInstanced",
         "displayInstancedThick",
      ];

      return function (base)
      {
         this .base = base;

         if (base)
         {
            for (const fn of functions)
               this [fn] = base [fn];
         }
         else
         {
            // Use default render functions.
            for (const fn of functions)
               delete this [fn];
         }
      };
   })(),
   displaySimple (gl, renderContext, shaderNode)
   {
      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         if (this .multiTexCoords .length)
            shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
      }

      gl .drawArrays (gl .TRIANGLES, 0, this .vertexCount);
   },
   display (gl, renderContext)
   {
      const
         { viewport, modelViewMatrix, appearanceNode } = renderContext,
         browser         = this .getBrowser (),
         primitiveMode   = browser .getPrimitiveMode (gl .TRIANGLES),
         renderModeNodes = appearanceNode .getRenderModes (),
         shaderNode      = appearanceNode .getShader (this, renderContext);

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Handle negative scale.

      const positiveScale = Numbers_Matrix4 .prototype .determinant3 .call (modelViewMatrix) >= 0;

      gl .frontFace (positiveScale ? this .frontFace : this .backFace);

      // Draw front and back faces.

      if (this .solid || !appearanceNode .getBackMaterial ())
      {
         this .displayGeometry (gl, renderContext, shaderNode, primitiveMode, true, true);
      }
      else
      {
         const backShaderNode = appearanceNode .getBackShader (this, renderContext);

         this .displayGeometry (gl, renderContext, backShaderNode, primitiveMode, true,  false);
         this .displayGeometry (gl, renderContext, shaderNode,     primitiveMode, false, true);
      }

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();
   },
   displayGeometry (gl, renderContext, shaderNode, primitiveMode, back, front)
   {
      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this, front);

      // Setup vertex attributes.

      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (this .colorMaterial)
            shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);

         if (this .hasTangents)
            shaderNode .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

         shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);
         shaderNode .enableNormalAttribute   (gl, this .normalBuffer,    0, 0);
         shaderNode .enableVertexAttribute   (gl, this .vertexBuffer,    0, 0);
      }

      // Draw depending on wireframe, solid and transparent.

      if (renderContext .transparent || back !== front)
      {
         // Render transparent or back or front.

         gl .enable (gl .CULL_FACE);

         // Render back.

         if (back && !this .solid)
         {
            gl .cullFace (gl .FRONT);
            gl .drawArrays (primitiveMode, 0, this .vertexCount);
         }

         // Render front.

         if (front)
         {
            gl .cullFace (gl .BACK);
            gl .drawArrays (primitiveMode, 0, this .vertexCount);
         }
      }
      else
      {
         // Render solid or both sides.

         if (this .solid)
            gl .enable (gl .CULL_FACE);
         else
            gl .disable (gl .CULL_FACE);

         gl .drawArrays (primitiveMode, 0, this .vertexCount);
      }
   },
   displaySimpleInstanced (gl, shaderNode, shapeNode)
   {
      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, particleOffset, matrixOffset, normalMatrixOffset } = shapeNode;

         if (particleOffset !== undefined)
            shaderNode .enableParticleAttribute (gl, instances, instancesStride, particleOffset, 1);

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);
         shaderNode .enableNormalAttribute   (gl, this .normalBuffer,    0, 0);
         shaderNode .enableVertexAttribute   (gl, this .vertexBuffer,    0, 0);

         this .updateInstances = false;
      }

      gl .drawArraysInstanced (gl .TRIANGLES, 0, this .vertexCount, shapeNode .getNumInstances ());
   },
   displayInstanced (gl, renderContext, shapeNode)
   {
      const
         { viewport, modelViewMatrix, appearanceNode } = renderContext,
         browser         = this .getBrowser (),
         primitiveMode   = browser .getPrimitiveMode (gl .TRIANGLES),
         renderModeNodes = appearanceNode .getRenderModes (),
         shaderNode      = appearanceNode .getShader (this, renderContext);

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Handle negative scale.

      const positiveScale = Numbers_Matrix4 .prototype .determinant3 .call (modelViewMatrix) >= 0;

      gl .frontFace (positiveScale ? this .frontFace : this .backFace);

      // Draw front and back faces.

      if (this .solid || !appearanceNode .getBackMaterial ())
      {
         this .displayInstancedGeometry (gl, renderContext, shaderNode, primitiveMode, true, true, shapeNode);
      }
      else
      {
         const backShaderNode = appearanceNode .getBackShader (this, renderContext);

         this .displayInstancedGeometry (gl, renderContext, backShaderNode, primitiveMode, true,  false, shapeNode);
         this .displayInstancedGeometry (gl, renderContext, shaderNode,     primitiveMode, false, true,  shapeNode);
      }

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();
   },
   displayInstancedGeometry (gl, renderContext, shaderNode, primitiveMode, back, front, shapeNode)
   {
      // Setup shader.

      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this, front);

      // Setup vertex attributes.

      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, particleOffset, velocityOffset, matrixOffset, normalMatrixOffset } = shapeNode;

         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (particleOffset !== undefined)
            shaderNode .enableParticleAttribute (gl, instances, instancesStride, particleOffset, 1);

         if (velocityOffset !== undefined)
            shaderNode .enableParticleVelocityAttribute (gl, instances, instancesStride, velocityOffset, 1);

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (this .colorMaterial)
            shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);

         if (this .hasTangents)
            shaderNode .enableTangentAttribute  (gl, this .tangentBuffer, 0, 0);

         shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);
         shaderNode .enableNormalAttribute   (gl, this .normalBuffer,    0, 0);
         shaderNode .enableVertexAttribute   (gl, this .vertexBuffer,    0, 0);

         this .updateInstances = false;
      }

      // Draw depending on wireframe, solid and transparent.

      if (renderContext .transparent || back !== front)
      {
         // Render transparent or back or front.

         gl .enable (gl .CULL_FACE);

         if (back && !this .solid)
         {
            gl .cullFace (gl .FRONT);
            gl .drawArraysInstanced (primitiveMode, 0, this .vertexCount, shapeNode .getNumInstances ());
         }

         if (front)
         {
            gl .cullFace (gl .BACK);
            gl .drawArraysInstanced (primitiveMode, 0, this .vertexCount, shapeNode .getNumInstances ());
         }
      }
      else
      {
         // Render solid or both sides.

         if (this .solid)
            gl .enable (gl .CULL_FACE);
         else
            gl .disable (gl .CULL_FACE);

         gl .drawArraysInstanced (primitiveMode, 0, this .vertexCount, shapeNode .getNumInstances ());
      }
   },
},
// Common functions for all X3DComposedGeometryNode types and some other nodes:
{
   getFogCoord ()
   {
      return this .fogCoordNode;
   },
   getColor ()
   {
      return this .colorNode;
   },
   getTexCoord ()
   {
      return this .texCoordNode;
   },
   getTangent ()
   {
      return this .tangentNode;
   },
   getNormal ()
   {
      return this .normalNode;
   },
   getCoord ()
   {
      return this .coordNode;
   },
   set_attrib__ ()
   {
      const attribNodes = this .getAttrib ();

      for (const attribNode of attribNodes)
      {
         attribNode .removeInterest ("requestRebuild", this);
         attribNode ._attribute_changed .removeInterest ("updateVertexArrays", this);
      }

      attribNodes .length = 0;

      for (const node of this ._attrib)
      {
         const attribNode = Base_X3DCast (Base_X3DConstants .X3DVertexAttributeNode, node);

         if (attribNode)
            attribNodes .push (attribNode);
      }

      for (const attribNode of attribNodes)
      {
         attribNode .addInterest ("requestRebuild", this);
         attribNode ._attribute_changed .addInterest ("updateVertexArrays", this);
      }

      this .updateVertexArrays ();
   },
   set_fogCoord__ ()
   {
      this .fogCoordNode ?.removeInterest ("requestRebuild", this);

      this .fogCoordNode = Base_X3DCast (Base_X3DConstants .FogCoordinate, this ._fogCoord);

      this .fogCoordNode ?.addInterest ("requestRebuild", this);
   },
   set_color__ ()
   {
      this .colorNode ?.removeInterest ("requestRebuild", this);

      this .colorNode = Base_X3DCast (Base_X3DConstants .X3DColorNode, this ._color);

      this .colorNode ?.addInterest ("requestRebuild", this);

      this .setTransparent (this .colorNode ?.isTransparent ());
   },
   set_texCoord__ ()
   {
      this .texCoordNode ?.removeInterest ("requestRebuild", this);

      this .texCoordNode = Base_X3DCast (Base_X3DConstants .X3DTextureCoordinateNode, this ._texCoord);

      this .texCoordNode ?.addInterest ("requestRebuild", this);

      this .setTextureCoordinate (this .texCoordNode);
   },
   set_tangent__ ()
   {
      this .tangentNode ?.removeInterest ("requestRebuild", this);

      this .tangentNode = Base_X3DCast (Base_X3DConstants .X3DTangentNode, this ._tangent);

      this .tangentNode ?.addInterest ("requestRebuild", this);
   },
   set_normal__ ()
   {
      this .normalNode ?.removeInterest ("requestRebuild", this);

      this .normalNode = Base_X3DCast (Base_X3DConstants .X3DNormalNode, this ._normal);

      this .normalNode ?.addInterest ("requestRebuild", this);
   },
   set_coord__ ()
   {
      this .coordNode ?.removeInterest ("requestRebuild", this);

      this .coordNode = Base_X3DCast (Base_X3DConstants .X3DCoordinateNode, this ._coord);

      this .coordNode ?.addInterest ("requestRebuild", this);
   },
});

Object .defineProperties (X3DGeometryNode, Core_X3DNode .getStaticProperties ("X3DGeometryNode", "Rendering", 1));

const X3DGeometryNode_default_ = X3DGeometryNode;
;

/* harmony default export */ const Rendering_X3DGeometryNode = (Namespace/* default */.A .add ("X3DGeometryNode", X3DGeometryNode_default_));
;// ./src/x_ite/Browser/Shape/AlphaMode.js
let AlphaMode_i = 0;

const AlphaMode =
{
   AUTO:   AlphaMode_i ++, // Must be zero!
   OPAQUE: AlphaMode_i ++,
   MASK:   AlphaMode_i ++,
   BLEND:  AlphaMode_i ++,
};

const AlphaMode_default_ = Object .assign (new Map (Object .entries (AlphaMode)), AlphaMode);
;

/* harmony default export */ const Shape_AlphaMode = (Namespace/* default */.A .add ("AlphaMode", AlphaMode_default_));
;// ./src/x_ite/Browser/Rendering/GeometryContext.js




function GeometryContext (options = { })
{
   Object .assign (this,
   {
      alphaMode: Shape_AlphaMode .OPAQUE,
      geometryType: 3,
      hasFogCoords: false,
      colorMaterial: false,
      hasNormals: false,
      hasTangents: false,
      textureNode: null,
      localObjectsKeys: [ ],
      textureCoordinateMapping: new Map (),
      textureCoordinateNode: null,
   },
   options)

   this .updateGeometryKey ();
}

Object .assign (GeometryContext .prototype,
{
   getMatrix ()
   {
      return Numbers_Matrix4 .IDENTITY;
   },
   getTextureCoordinateMapping ()
   {
      return this .textureCoordinateMapping;
   },
   getTextureCoordinate ()
   {
      return this .textureCoordinateNode;
   },
   updateGeometryKey: Rendering_X3DGeometryNode .prototype .updateGeometryKey,
});

const GeometryContext_default_ = GeometryContext;
;

/* harmony default export */ const Rendering_GeometryContext = (Namespace/* default */.A .add ("GeometryContext", GeometryContext_default_));
;// ./src/standard/Math/Numbers/Complex.js
function Complex (real = 0, imag = 0)
{
   this .real = real;
   this .imag = imag;
}

Object .assign (Complex .prototype,
{
   *[Symbol .iterator] ()
   {
      yield this .real;
      yield this .imag;
   },
   copy ()
   {
      const copy = Object .create (Complex .prototype);
      copy .real = this .real;
      copy .imag = this .imag;
      return copy;
   },
   assign (complex)
   {
      this .real = complex .real;
      this .imag = complex .imag;
      return this;
   },
   equals (complex)
   {
      return this .real === complex .real &&
             this .imag === complex .imag;
   },
   set (real = 0, imag = 0)
   {
      this .real = real;
      this .imag = imag;
      return this;
   },
   setPolar (magnitude, angle)
   {
      this .real = magnitude * Math .cos (angle);
      this .imag = magnitude * Math .sin (angle);
      return this;
   },
   conjugate ()
   {
      this .imag = -this .imag;
      return this;
   },
   negate ()
   {
      this .real = -this .real;
      this .imag = -this .imag;
      return this;
   },
   inverse ()
   {
      const d = this .real * this .real + this .imag * this .imag;

      this .real /=  d;
      this .imag /= -d;
      return this;
   },
   add (value)
   {
      this .real += value .real;
      this .imag += value .imag;
      return this;
   },
   subtract (value)
   {
      this .real -= value .real;
      this .imag -= value .imag;
      return this;
   },
   multiply (value)
   {
      this .real *= value;
      this .imag *= value;
      return this;
   },
   multComp (value)
   {
      const
         { real: ar, imag: ai } = this,
         { real: br, imag: bi } = value;

      this .real = ar * br - ai * bi;
      this .imag = ar * bi + ai * br;
      return this;
   },
   divide (value)
   {
      this .real /= value;
      this .imag /= value;
      return this;
   },
   divComp (value)
   {
      const
         { real: ar, imag: ai } = this,
         { real: br, imag: bi } = value,
         d = br * br + bi * bi;

      this .real = (ar * br + ai * bi) / d;
      this .imag = (ai * br - ar * bi) / d;
      return this;
   },
   toString ()
   {
      let string = "";

      string += this .real;

      if (this .imag < 0)
      {
         string += this .imag;
         string += "i";
      }
      else if (this .imag > 0)
      {
         string += "+";
         string += this .imag;
         string += "i";
      }

      return string;
   },
});

for (const key of Object .keys (Complex .prototype))
   Object .defineProperty (Complex .prototype, key, { enumerable: false });

Object .defineProperties (Complex .prototype,
{
   length: { value: 2 },
   0:
   {
      get ()
      {
         return this .real;
      },
      set (value)
      {
         this .real = value;
      },
   },
   1:
   {
      get ()
      {
         return this .imag;
      },
      set (value)
      {
         this .imag = value;
      },
   },
   magnitude:
   {
      get ()
      {
         if (this .real)
         {
            if (this .imag)
               return Math .hypot (this .real, this .imag);

            return Math .abs (this .real);
         }

         return Math .abs (this .imag);
      },
      set (magnitude)
      {
         this .setPolar (magnitude, this .angle);
      },
   },
   angle:
   {
      get ()
      {
         return Math .atan2 (this .imag, this .real);
      },
      set (angle)
      {
         this .setPolar (this .magnitude, angle);
      },
   },
});

Object .assign (Complex,
{
   fromPolar (magnitude, angle)
   {
      return Object .create (Complex .prototype) .setPolar (magnitude, angle);
   },
});

const Complex_default_ = Complex;
;

/* harmony default export */ const Numbers_Complex = (Namespace/* default */.A .add ("Complex", Complex_default_));
;// ./src/standard/Utility/BitSet.js


const BitSet_value = Symbol ();

function BitSet (value = 0)
{
   this [BitSet_value] = value >>> 0;
}

Object .assign (BitSet .prototype,
{
   *[Symbol .iterator] ()
   {
      let
         value = this [BitSet_value],
         index = 0;

      while (value)
      {
         if (value & 1)
            yield index;

         value >>>= 1;
         ++ index;
      }
   },
   get (index)
   {
      const mask = 1 << index;

      return !! (this [BitSet_value] & mask);
   },
   set (index, value)
   {
      if (value)
         this .add (index);
      else
         this .remove (index);
   },
   add (index, mask = 1)
   {
      this [BitSet_value] |= mask << index;
      this [BitSet_value] >>>= 0;
   },
   remove (index, mask = 1)
   {
      this [BitSet_value] &= ~(mask << index);
      this [BitSet_value] >>>= 0;
   },
   clear ()
   {
      this [BitSet_value] = 0;
   },
   *entries ()
   {
      for (const i of this)
         yield [i, i];
   },
   valueOf ()
   {
      return this [BitSet_value];
   },
   toString (radix)
   {
      return this [BitSet_value] .toString (radix);
   },
});

BitSet .prototype .keys   = BitSet .prototype [Symbol .iterator];
BitSet .prototype .values = BitSet .prototype [Symbol .iterator];

Object .defineProperty (BitSet .prototype, "size",
{
   get ()
   {
      return Math_Algorithm .bitCount (this [BitSet_value]);
   },
});

const BitSet_default_ = BitSet;
;

/* harmony default export */ const Utility_BitSet = (Namespace/* default */.A .add ("BitSet", BitSet_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/X3DBackgroundNode.js

















function X3DBackgroundNode (executionContext)
{
   Core_X3DBindableNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DBackgroundNode);

   this .addChildObjects (Base_X3DConstants .inputOutput, "hidden", new x_ite_Fields .SFBool ());

   this .setVisibleObject (true);

   // Units

   this ._skyAngle    .setUnit ("angle");
   this ._groundAngle .setUnit ("angle");

   // Private properties

   this .modelMatrix      = new Numbers_Matrix4 ();
   this .localObjects     = [ ];
   this .localObjectsKeys = [ ];
   this .colors           = [ ];
   this .sphere           = [ ];
   this .textureNodes     = new Array (6);
   this .textureBits      = new Utility_BitSet ();
   this .sphereContext    = new Rendering_GeometryContext ({ colorMaterial: true, localObjectsKeys: this .localObjectsKeys });
   this .texturesContext  = new Rendering_GeometryContext ({ localObjectsKeys: this .localObjectsKeys });

   this [Rendering_RenderPass .RENDER_KEY]         = this;
   this [Rendering_RenderPass .TRANSMISSION_KEY]   = this;
   this [Rendering_RenderPass .VOLUME_SCATTER_KEY] = null;

}

Object .assign (Object .setPrototypeOf (X3DBackgroundNode .prototype, Core_X3DBindableNode .prototype),
{
   initialize ()
   {
      Core_X3DBindableNode .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         gl      = browser .getContext ();

      this .colorBuffer         = gl .createBuffer ();
      this .sphereBuffer        = gl .createBuffer ();
      this .texCoordBuffers     = [gl .createBuffer ()];
      this .textureBuffers      = Array .from ({length: 6}, () => gl .createBuffer ());
      this .sphereArrayObject   = new Rendering_VertexArray (gl);
      this .textureArrayObjects = Array .from ({length: 6}, () => new Rendering_VertexArray (gl));

      this ._groundAngle .addInterest ("build", this);
      this ._groundColor .addInterest ("build", this);
      this ._skyAngle    .addInterest ("build", this);
      this ._skyColor    .addInterest ("build", this);

      this .build ();
      this .transferRectangle ();
   },
   isHidden ()
   {
      return this ._hidden .getValue ();
   },
   setHidden (value)
   {
      if (value === this ._hidden .getValue ())
         return;

      this ._hidden = value;
   },
   isTransparent ()
   {
      if (this ._hidden .getValue ())
         return true;

      if (this ._transparency .getValue () <= 0)
         return false;

      if (this .textureBits .size !== 6)
         return true;

      for (const i of this .textureBits)
      {
         if (this .textureNodes [i] ._transparent .getValue ())
            return true;
      }

      return false;
   },
   getRenderPassNodes ()
   {
      return this .renderPassNodes;
   },
   getTextureNodes ()
   {
      return this .textureNodes;
   },
   set_texture__ (index, textureNode)
   {
      this .textureNodes [index] ?.removeInterest ("set_loadState__", this);

      this .textureNodes [index] = textureNode;

      textureNode ?.addInterest ("set_loadState__", this, index, textureNode);

      this .set_loadState__ (index, textureNode);
   },
   set_loadState__ (index, textureNode)
   {
      this .setTextureBit (index, textureNode ?.checkLoadState ());
   },
   setTextureBit (bit, loadState)
   {
      this .textureBits .set (bit, loadState === Base_X3DConstants .COMPLETE_STATE);
   },
   getColor (theta, color, angle)
   {
      const index = Math_Algorithm .upperBound (angle, 0, angle .length, theta);

      return color [index];
   },
   build ()
   {
      this .colors .length = 0;
      this .sphere .length = 0;

      if (this ._groundColor .length === 0 && this ._skyColor .length == 1)
      {
         // Build cube

         this .sphere .vertices = 36;

         this .sphere .push ( 1,  1, -1, 1, -1,  1, -1, 1, -1, -1, -1, 1, // Back
                              1,  1, -1, 1, -1, -1, -1, 1,  1, -1, -1, 1,
                             -1,  1,  1, 1,  1,  1,  1, 1, -1, -1,  1, 1, // Front
                             -1, -1,  1, 1,  1,  1,  1, 1,  1, -1,  1, 1,
                             -1,  1, -1, 1, -1,  1,  1, 1, -1, -1,  1, 1, // Left
                             -1,  1, -1, 1, -1, -1,  1, 1, -1, -1, -1, 1,
                              1,  1,  1, 1,  1,  1, -1, 1,  1, -1,  1, 1, // Right
                              1, -1,  1, 1,  1,  1, -1, 1,  1, -1, -1, 1,
                              1,  1,  1, 1, -1,  1,  1, 1, -1,  1, -1, 1, // Top
                              1,  1,  1, 1, -1,  1, -1, 1,  1,  1, -1, 1,
                             -1, -1,  1, 1,  1, -1,  1, 1, -1, -1, -1, 1, // Bottom
                             -1, -1, -1, 1,  1, -1,  1, 1,  1, -1, -1, 1);

         const color = this ._skyColor [0];

         for (let i = 0, vertices = this .sphere .vertices; i < vertices; ++ i)
            this .colors .push (... color, 1);
      }
      else
      {
         // Build sphere

         if (this ._skyColor .length > this ._skyAngle .length)
         {
            const vAngle = this ._skyAngle .slice ();

            if (vAngle .length === 0 || vAngle [0] > 0)
               vAngle .unshift (0);

            if (vAngle .at (-1) < Math .PI)
               vAngle .push (Math .PI);

            if (vAngle .length === 2)
               vAngle .splice (1, 0, (vAngle [0] + vAngle [1]) / 2)

            this .buildSphere (vAngle, this ._skyAngle, this ._skyColor, false);
         }

         if (this ._groundColor .length > this ._groundAngle .length)
         {
            const vAngle = this ._groundAngle .slice () .reverse ();

            if (vAngle .length === 0 || vAngle [0] < Math .PI / 2)
               vAngle .unshift (Math .PI / 2);

            if (vAngle .at (-1) > 0)
               vAngle .push (0);

            this .buildSphere (vAngle, this ._groundAngle, this ._groundColor, true);
         }
      }

      this .transferSphere ();
   },
   buildSphere: (() =>
   {
      const U_DIMENSION = 20;

      const
         z1 = new Numbers_Complex (),
         z2 = new Numbers_Complex (),
         y1 = new Numbers_Complex (),
         y2 = new Numbers_Complex (),
         y3 = new Numbers_Complex (),
         y4 = new Numbers_Complex ();

      return function (vAngle, angle, color, bottom)
      {
         const
            vAngleMax   = bottom ? Math .PI / 2 : Math .PI,
            V_DIMENSION = vAngle .length - 1;

         for (let v = 0; v < V_DIMENSION; ++ v)
         {
            let
               theta1 = Math_Algorithm .clamp (vAngle [v],     0, vAngleMax),
               theta2 = Math_Algorithm .clamp (vAngle [v + 1], 0, vAngleMax);

            if (bottom)
            {
               theta1 = Math .PI - theta1;
               theta2 = Math .PI - theta2;
            }

            z1 .setPolar (1, theta1);
            z2 .setPolar (1, theta2);

            const
               c1 = this .getColor (vAngle [v],     color, angle),
               c2 = this .getColor (vAngle [v + 1], color, angle);

            for (let u = 0; u < U_DIMENSION; ++ u)
            {
               // p4 --- p1
               //  |   / |
               //  | /   |
               // p3 --- p2

               // The last point is the first one.
               const u1 = u < U_DIMENSION - 1 ? u + 1 : 0;

               // p1, p2
               let phi = 2 * Math .PI * (u / U_DIMENSION);
               y1 .setPolar (-z1 .imag, phi);
               y2 .setPolar (-z2 .imag, phi);

               // p3, p4
               phi = 2 * Math .PI * (u1 / U_DIMENSION);
               y3 .setPolar (-z2 .imag, phi);
               y4 .setPolar (-z1 .imag, phi);

               // Triangle 1 and 2

               this .colors .push (... c1, 1,
                                   ... c2, 1,
                                   ... c2, 1,
                                   // Triangle 2
                                   ... c1, 1,
                                   ... c1, 1,
                                   ... c2, 1);

               this .sphere .push (y1 .imag, z1 .real, y1 .real, 1,
                                   y3 .imag, z2 .real, y3 .real, 1,
                                   y2 .imag, z2 .real, y2 .real, 1,
                                   // Triangle 2
                                   y1 .imag, z1 .real, y1 .real, 1,
                                   y4 .imag, z1 .real, y4 .real, 1,
                                   y3 .imag, z2 .real, y3 .real, 1);
            }
         }
      };
   })(),
   transferSphere ()
   {
      const gl = this .getBrowser () .getContext ();

      // Transfer colors.

      gl .bindBuffer (gl .ARRAY_BUFFER, this .colorBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, new Float32Array (this .colors), gl .DYNAMIC_DRAW);

      // Transfer sphere.

      gl .bindBuffer (gl .ARRAY_BUFFER, this .sphereBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, new Float32Array (this .sphere), gl .DYNAMIC_DRAW);

      this .sphereCount = this .sphere .length / 4;
   },
   transferRectangle: (() =>
   {
      const texCoords = new Float32Array ([
         1, 1, 0, 1,
         0, 1, 0, 1,
         0, 0, 0, 1,
         1, 1, 0, 1,
         0, 0, 0, 1,
         1, 0, 0, 1,
      ]);

      const frontVertices = new Float32Array ([
         1,  1, -1, 1,
        -1,  1, -1, 1,
        -1, -1, -1, 1,
         1,  1, -1, 1,
        -1, -1, -1, 1,
         1, -1, -1, 1,
      ]);

      const backVertices = new Float32Array ([
         -1,  1,  1, 1,
          1,  1,  1, 1,
          1, -1,  1, 1,
         -1,  1,  1, 1,
          1, -1,  1, 1,
         -1, -1,  1, 1,
      ]);

      const leftVertices = new Float32Array ([
         -1,  1, -1, 1,
         -1,  1,  1, 1,
         -1, -1,  1, 1,
         -1,  1, -1, 1,
         -1, -1,  1, 1,
         -1, -1, -1, 1,
      ]);

      const rightVertices = new Float32Array ([
         1,  1,  1, 1,
         1,  1, -1, 1,
         1, -1, -1, 1,
         1,  1,  1, 1,
         1, -1, -1, 1,
         1, -1,  1, 1,
      ]);

      const topVertices = new Float32Array ([
          1, 1,  1, 1,
         -1, 1,  1, 1,
         -1, 1, -1, 1,
          1, 1,  1, 1,
         -1, 1, -1, 1,
          1, 1, -1, 1,
      ]);

      const bottomVertices = new Float32Array ([
          1, -1, -1, 1,
         -1, -1, -1, 1,
         -1, -1,  1, 1,
          1, -1, -1, 1,
         -1, -1,  1, 1,
          1, -1,  1, 1,
      ]);

      const vertices = [
         frontVertices,
         backVertices,
         leftVertices,
         rightVertices,
         topVertices,
         bottomVertices,
      ];

      return function ()
      {
         const gl = this .getBrowser () .getContext ();

         // Transfer texCoords.

         gl .bindBuffer (gl .ARRAY_BUFFER, this .texCoordBuffers [0]);
         gl .bufferData (gl .ARRAY_BUFFER, texCoords, gl .DYNAMIC_DRAW);

         // Transfer rectangle.

         for (let i = 0; i < 6; ++ i)
         {
            gl .bindBuffer (gl .ARRAY_BUFFER, this .textureBuffers [i]);
            gl .bufferData (gl .ARRAY_BUFFER, vertices [i], gl .DYNAMIC_DRAW);
         }
      };
   })(),
   traverse (type, renderObject)
   {
      switch (type)
      {
         case Rendering_TraverseType .CAMERA:
         {
            renderObject .getLayer () .getBackgrounds () .push (this);

            this .modelMatrix .assign (renderObject .getModelViewMatrix () .get ());
            return;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            // Handle ClipPane nodes.
            Rendering_X3DRenderObject .assign (this .localObjects,     renderObject .getLocalObjects ());
            Rendering_X3DRenderObject .assign (this .localObjectsKeys, renderObject .getLocalObjectsKeys ());
            return;
         }
      }
   },
   display: (() =>
   {
      const
         projectionMatrixArray = new Float32Array (16),
         projectionMatrix      = new Numbers_Matrix4 (),
         projectionScale       = new Numbers_Matrix4 (1,0,0,0, 0,1,0,0, 0,0,0,0, 0,0,0,1),
         modelViewMatrixArray  = new Float32Array (16),
         modelViewMatrix       = new Numbers_Matrix4 (),
         rotation              = new Numbers_Rotation4 (),
         scale                 = new Numbers_Vector3 ();

      return function (gl, renderObject)
      {
         if (this ._hidden .getValue ())
            return;

         const browser = this .getBrowser ();

         // Always fill background.

         if (browser .getWireframe ())
         {
            const ext = gl .getExtension ("WEBGL_polygon_mode");

            ext ?.polygonModeWEBGL (gl .FRONT_AND_BACK, ext .FILL_WEBGL);
         }

         // Setup context.

         gl .disable (gl .DEPTH_TEST);
         gl .depthMask (false);
         gl .enable (gl .CULL_FACE);
         gl .frontFace (gl .CCW);

         // Create projection matrix.
         // The projectionScale will set gl_Position.z to 0,
         // so it is in the middle of near and far plane.

         projectionMatrixArray .set (projectionMatrix
            .assign (renderObject .getProjectionMatrixArray ())
            .multRight (projectionScale));

         // Rotate and scale background.

         const far = renderObject .getViewpoint () .getMaxFarValue ();

         modelViewMatrix .assign (this .modelMatrix);
         modelViewMatrix .multRight (renderObject .getViewMatrix () .get ());
         modelViewMatrix .getTransform (null, rotation);
         modelViewMatrix .set ();
         modelViewMatrix .rotate (rotation);
         modelViewMatrix .scale (scale .set (far, far, far));

         modelViewMatrixArray .set (modelViewMatrix);

         // Draw background sphere and texture cube.

         this .drawSphere (renderObject, modelViewMatrixArray, projectionMatrixArray);

         if (+this .textureBits)
            this .drawCube (renderObject, modelViewMatrixArray, projectionMatrixArray);

         gl .depthMask (true);
         gl .enable (gl .DEPTH_TEST);
         gl .disable (gl .BLEND);

         // Restore polygon mode.

         if (browser .getWireframe ())
         {
            const ext = gl .getExtension ("WEBGL_polygon_mode");

            ext ?.polygonModeWEBGL (gl .FRONT_AND_BACK, ext .LINE_WEBGL);
         }
      };
   })(),
   drawSphere (renderObject, modelViewMatrixArray, projectionMatrixArray)
   {
      const transparency = Math_Algorithm .clamp (this ._transparency .getValue (), 0, 1);

      if (transparency === 1)
         return;

      const
         browser       = this .getBrowser (),
         gl            = browser .getContext (),
         sphereContext = this .sphereContext;

      sphereContext .alphaMode    = transparency ? Shape_AlphaMode .BLEND : Shape_AlphaMode .OPAQUE;
      sphereContext .renderObject = renderObject;

      const shaderNode = browser .getDefaultMaterial () .getShader (sphereContext);

      shaderNode .enable (gl);
      shaderNode .setClipPlanes (gl, this .localObjects, renderObject);

      // Uniforms

      gl .uniformMatrix4fv (shaderNode .x3d_ProjectionMatrix, false, projectionMatrixArray);
      gl .uniformMatrix4fv (shaderNode .x3d_EyeMatrix,        false, renderObject .getEyeMatrixArray ());
      gl .uniformMatrix4fv (shaderNode .x3d_ModelViewMatrix,  false, modelViewMatrixArray);

      gl .uniform3f (shaderNode .x3d_EmissiveColor,                        1, 1, 1)
      gl .uniform1f (shaderNode .x3d_Transparency,                         transparency)
      gl .uniform1i (shaderNode .x3d_TextureCoordinateGenerator [0] .mode, 0);

      // Enable vertex attribute arrays.

      if (this .sphereArrayObject .enable (shaderNode .getProgram ()))
      {
         shaderNode .enableColorAttribute  (gl, this .colorBuffer,  0, 0);
         shaderNode .enableVertexAttribute (gl, this .sphereBuffer, 0, 0);
      }

      // Draw.

      if (transparency)
         gl .enable (gl .BLEND);
      else
         gl .disable (gl .BLEND);

      gl .drawArrays (gl .TRIANGLES, 0, this .sphereCount);
   },
   drawCube: (() =>
   {
      const textureMatrixArray = new Float32Array (Numbers_Matrix4 .IDENTITY);

      return function (renderObject, modelViewMatrixArray, projectionMatrixArray)
      {
         const
            browser         = this .getBrowser (),
            gl              = browser .getContext (),
            texturesContext = this .texturesContext;

         // Draw all textures.

         for (const i of this .textureBits)
         {
            const textureNode = this .textureNodes [i];

            texturesContext .alphaMode    = textureNode ._transparent .getValue () ? Shape_AlphaMode .BLEND : Shape_AlphaMode .OPAQUE;
            texturesContext .textureNode  = textureNode;
            texturesContext .renderObject = renderObject;

            const shaderNode = browser .getDefaultMaterial () .getShader (texturesContext);

            shaderNode .enable (gl);
            shaderNode .setClipPlanes (gl, this .localObjects, renderObject);

            // Set uniforms.

            gl .uniformMatrix4fv (shaderNode .x3d_ProjectionMatrix,  false, projectionMatrixArray);
            gl .uniformMatrix4fv (shaderNode .x3d_EyeMatrix,         false, renderObject .getEyeMatrixArray ());
            gl .uniformMatrix4fv (shaderNode .x3d_ModelViewMatrix,   false, modelViewMatrixArray);
            gl .uniformMatrix4fv (shaderNode .x3d_TextureMatrix [0], false, textureMatrixArray);

            gl .uniform3f (shaderNode .x3d_EmissiveColor,                        1, 1, 1);
            gl .uniform1f (shaderNode .x3d_Transparency,                         0);
            gl .uniform1i (shaderNode .x3d_TextureCoordinateGenerator [0] .mode, 0);

            this .drawRectangle (gl, browser, shaderNode, textureNode, this .textureBuffers [i], this .textureArrayObjects [i]);
         }
      };
   })(),
   drawRectangle (gl, browser, shaderNode, textureNode, buffer, vertexArray)
   {
      textureNode .setShaderUniforms (gl, shaderNode .x3d_Texture [0]);

      if (vertexArray .enable (shaderNode .getProgram ()))
      {
         shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);
         shaderNode .enableVertexAttribute (gl, buffer, 0, 0);
      }

      // Draw.

      if (textureNode ._transparent .getValue ())
         gl .enable (gl .BLEND);
      else
         gl .disable (gl .BLEND);

      gl .drawArrays (gl .TRIANGLES, 0, 6);

      browser .resetTextureUnits ();
   },
});

Object .defineProperties (X3DBackgroundNode, Core_X3DNode .getStaticProperties ("X3DBackgroundNode", "EnvironmentalEffects", 1));

const X3DBackgroundNode_default_ = X3DBackgroundNode;
;

/* harmony default export */ const EnvironmentalEffects_X3DBackgroundNode = (Namespace/* default */.A .add ("X3DBackgroundNode", X3DBackgroundNode_default_));
;// ./src/x_ite/Components/Shape/X3DAppearanceChildNode.js



function X3DAppearanceChildNode (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DAppearanceChildNode);
}

Object .setPrototypeOf (X3DAppearanceChildNode .prototype, Core_X3DNode .prototype);

Object .defineProperties (X3DAppearanceChildNode, Core_X3DNode .getStaticProperties ("X3DAppearanceChildNode", "Shape", 1));

const X3DAppearanceChildNode_default_ = X3DAppearanceChildNode;
;

/* harmony default export */ const Shape_X3DAppearanceChildNode = (Namespace/* default */.A .add ("X3DAppearanceChildNode", X3DAppearanceChildNode_default_));
;// ./src/x_ite/Components/Texturing/X3DTextureNode.js





function X3DTextureNode (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTextureNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (X3DTextureNode .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
});

Object .defineProperties (X3DTextureNode, Core_X3DNode .getStaticProperties ("X3DTextureNode", "Texturing", 1));

const X3DTextureNode_default_ = X3DTextureNode;
;

/* harmony default export */ const Texturing_X3DTextureNode = (Namespace/* default */.A .add ("X3DTextureNode", X3DTextureNode_default_));
;// ./src/x_ite/Components/Texturing/X3DSingleTextureNode.js








function X3DSingleTextureNode (executionContext)
{
   Texturing_X3DTextureNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSingleTextureNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "linear", new x_ite_Fields .SFBool ())

   // Private properties

   this .matrix = new Float32Array (Numbers_Matrix4 .IDENTITY);
}

Object .assign (Object .setPrototypeOf (X3DSingleTextureNode .prototype, Texturing_X3DTextureNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTextureNode .prototype .initialize .call (this);

      this ._textureProperties .addInterest ("set_textureProperties__", this, true);

      const gl = this .getBrowser () .getContext ();

      this .texture = gl .createTexture ();

      this .set_textureProperties__ (false);
   },
   getCount ()
   {
      return 1;
   },
   getTextureTypeString ()
   {
      switch (this .getTextureType ())
      {
         case 1:                // ImageTexture, MovieTexture (flipY)
         case 2: return "2D";   // PixelTexture
         case 3: return "3D";   // X3DTexture3DNode
         case 4: return "CUBE"; // X3DEnvironmentTextureNode
      }
   },
   getTexture ()
   {
      return this .texture;
   },
   setTexture (texture)
   {
      const gl = this .getBrowser () .getContext ();

      gl .deleteTexture (this .texture);

      this .texture = texture;

      this .addNodeEvent ();
   },
   isLinear ()
   {
      return this ._linear .getValue ();
   },
   setLinear (value)
   {
      if (!!value !== this ._linear .getValue ())
         this ._linear = value;
   },
   canMipMaps ()
   {
      return this .mipMaps;
   },
   setMipMaps (value)
   {
      this .mipMaps = value;
   },
   getMatrix ()
   {
      // Normally the identity matrix or a flipY matrix.
      return this .matrix;
   },
   isRenderedTexture ()
   {
      return false;
   },
   getRenderedTextures (renderedTextures)
   { },
   isImageTransparent (data)
   {
      const length = data .length;

      for (let i = 3; i < length; i += 4)
      {
         if (data [i] !== 255)
            return true;
      }

      return false;
   },
   set_textureProperties__ (update)
   {
      if (this .texturePropertiesNode)
         this .texturePropertiesNode .removeInterest ("updateTextureParameters", this);

      this .texturePropertiesNode = Base_X3DCast (Base_X3DConstants .TextureProperties, this ._textureProperties)
         ?? this .getBrowser () .getDefaultTextureProperties ();

      this .texturePropertiesNode .addInterest ("updateTextureParameters", this);

      if (update)
         this .updateTextureParameters ();
   },
   updateTextureParameters (target, haveTextureProperties, textureProperties, width, height, repeatS, repeatT, repeatR)
   {
      const
         browser = this .getBrowser (),
         gl      = browser .getContext ();

      gl .bindTexture (target, this .getTexture ());

      if (!haveTextureProperties && Math .max (width, height) < browser .getMinTextureSize ())
      {
         // Don't generate MipMaps.
         gl .texParameteri (target, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
         gl .texParameteri (target, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
      }
      else if (this .canMipMaps () && textureProperties ._generateMipMaps .getValue ())
      {
         // Can MipMaps and wants MipMaps.
         gl .generateMipmap (target);

         gl .texParameteri (target, gl .TEXTURE_MIN_FILTER, gl [textureProperties .getMinificationFilter ()]);
         gl .texParameteri (target, gl .TEXTURE_MAG_FILTER, gl [textureProperties .getMagnificationFilter ()]);
      }
      else
      {
         // No MipMaps.
         gl .texParameteri (target, gl .TEXTURE_MIN_FILTER, gl [textureProperties .getMinificationFilter (false)]);
         gl .texParameteri (target, gl .TEXTURE_MAG_FILTER, gl [textureProperties .getMagnificationFilter (false)]);
      }

      if (haveTextureProperties)
      {
         gl .texParameteri (target, gl .TEXTURE_WRAP_S, gl [textureProperties .getBoundaryModeS ()]);
         gl .texParameteri (target, gl .TEXTURE_WRAP_T, gl [textureProperties .getBoundaryModeT ()]);
         gl .texParameteri (target, gl .TEXTURE_WRAP_R, gl [textureProperties .getBoundaryModeR ()]);
      }
      else
      {
         gl .texParameteri (target, gl .TEXTURE_WRAP_S, repeatS ? gl .REPEAT : gl .CLAMP_TO_EDGE);
         gl .texParameteri (target, gl .TEXTURE_WRAP_T, repeatT ? gl .REPEAT : gl .CLAMP_TO_EDGE);
         gl .texParameteri (target, gl .TEXTURE_WRAP_R, repeatR ? gl .REPEAT : gl .CLAMP_TO_EDGE);
      }

      //gl .texParameterfv (target, gl .TEXTURE_BORDER_COLOR, textureProperties ._borderColor .getValue ());
      //gl .texParameterf  (target, gl .TEXTURE_PRIORITY,     textureProperties ._texturePriority .getValue ());

      const ext = browser .getAnisotropicExtension ();

      if (ext)
      {
         const max = gl .getParameter (ext .MAX_TEXTURE_MAX_ANISOTROPY_EXT);

         gl .texParameterf (target, ext .TEXTURE_MAX_ANISOTROPY_EXT, Math_Algorithm .clamp (textureProperties ._anisotropicDegree .getValue (), 0, max));
      }
   },
   getTextureBits ()
   {
      return (this .isLinear () << 3) | this .getTextureType ();
   },
   updateTextureBits (textureBits, channel = 0)
   {
      textureBits .add (channel * 4, this .getTextureBits ());
   },
   getShaderOptions (options, name = 0, ext = false)
   {
      if (typeof name === "number")
      {
         options .push (`X3D_TEXTURE_${name}_${this .getTextureTypeString ()}`);

         if (this .getTextureType () === 1)
            options .push (`X3D_TEXTURE_${name}_FLIP_Y`);

         if (this .isLinear ())
            options .push (`X3D_TEXTURE_${name}_LINEAR`);
      }
      else
      {
         ext = ext ? "_EXT" : "";

         options .push (`X3D_${name}_TEXTURE${ext}`, `X3D_${name}_TEXTURE${ext}_${this .getTextureTypeString ()}`);

         if (this .getTextureType () === 1)
            options .push (`X3D_${name}_TEXTURE${ext}_FLIP_Y`);

         if (this .isLinear ())
            options .push (`X3D_${name}_TEXTURE${ext}_LINEAR`);
      }
   },
   setNamedShaderUniforms (gl, uniformStruct, mapping, textureTransformMapping, textureCoordinateMapping)
   {
      this .setShaderUniforms (gl, uniformStruct);

      gl .uniform1i (uniformStruct .textureTransformMapping,  textureTransformMapping  .get (mapping) ?? 0);
      gl .uniform1i (uniformStruct .textureCoordinateMapping, textureCoordinateMapping .get (mapping) ?? 0);
   },
});

Object .defineProperties (X3DSingleTextureNode, Core_X3DNode .getStaticProperties ("X3DSingleTextureNode", "Texturing", 1));

const X3DSingleTextureNode_default_ = X3DSingleTextureNode;
;

/* harmony default export */ const Texturing_X3DSingleTextureNode = (Namespace/* default */.A .add ("X3DSingleTextureNode", X3DSingleTextureNode_default_));
;// ./src/x_ite/Components/Texturing/X3DTexture2DNode.js




const defaultData = new Uint8Array ([ 255, 255, 255, 255 ]);

function X3DTexture2DNode (executionContext)
{
   Texturing_X3DSingleTextureNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTexture2DNode);

   // Private properties

   const gl = this .getBrowser () .getContext ();

   this .target = gl .TEXTURE_2D;
   this .width  = 0;
   this .height = 0;
}

Object .assign (Object .setPrototypeOf (X3DTexture2DNode .prototype, Texturing_X3DSingleTextureNode .prototype),
{
   initialize ()
   {
      Texturing_X3DSingleTextureNode .prototype .initialize .call (this);

      this ._repeatS .addInterest ("updateTextureParameters", this);
      this ._repeatT .addInterest ("updateTextureParameters", this);

      const gl = this .getBrowser () .getContext ();

      gl .bindTexture (gl .TEXTURE_2D, this .getTexture ());
      gl .texImage2D  (gl .TEXTURE_2D, 0, gl .RGBA, 1, 1, 0, gl .RGBA, gl .UNSIGNED_BYTE, defaultData);
   },
   getTarget ()
   {
      return this .target;
   },
   getWidth ()
   {
      return this .width;
   },
   setWidth (value)
   {
      this .width = value;
   },
   getHeight ()
   {
      return this .height;
   },
   setHeight (value)
   {
      this .height = value;
   },
   clearTexture ()
   {
      this .setTextureData (1, 1, false, false, defaultData);
   },
   getTextureData (texture = this .getTexture (), width = this .getWidth (), height = this .getHeight ())
   {
      const
         gl          = this .getBrowser () .getContext (),
         framebuffer = gl .createFramebuffer (),
         data        = new Uint8Array (width * height * 4);

      gl .bindFramebuffer (gl .FRAMEBUFFER, framebuffer);
      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .TEXTURE_2D, texture, 0);
      gl .readPixels (0, 0, width, height, gl .RGBA, gl .UNSIGNED_BYTE, data);
      gl .deleteFramebuffer (framebuffer);

      return data;
   },
   setTextureData (width, height, colorSpaceConversion, transparent, data)
   {
      this .width  = width;
      this .height = height;

      const
         gl  = this .getBrowser () .getContext (),
         max = gl .getParameter (gl .MAX_TEXTURE_SIZE);

      if (width > max || height > max)
      {
         throw new Error (`At least one dimension (${width} × ${height}) is greater than the maximum texture size (${max} px).`);
      }

      gl .bindTexture (gl .TEXTURE_2D, this .getTexture ());
      gl .pixelStorei (gl .UNPACK_COLORSPACE_CONVERSION_WEBGL, colorSpaceConversion ? gl .BROWSER_DEFAULT_WEBGL : gl .NONE);
      gl .texImage2D  (gl .TEXTURE_2D, 0, gl .RGBA, width, height, 0, gl .RGBA, gl .UNSIGNED_BYTE, data);
      gl .pixelStorei (gl .UNPACK_COLORSPACE_CONVERSION_WEBGL, gl .BROWSER_DEFAULT_WEBGL);

      this .setTransparent (transparent);
      this .updateTextureParameters ();
      this .addNodeEvent ();
   },
   updateTextureData (data)
   {
      const gl = this .getBrowser () .getContext ();

      gl .bindTexture (gl .TEXTURE_2D, this .getTexture ());
      gl .texSubImage2D (gl .TEXTURE_2D, 0, 0, 0, gl .RGBA, gl .UNSIGNED_BYTE, data);

      if (this .texturePropertiesNode ._generateMipMaps .getValue ())
         gl .generateMipmap (gl .TEXTURE_2D);

      this .addNodeEvent ();
   },
   updateTextureParameters ()
   {
      Texturing_X3DSingleTextureNode .prototype .updateTextureParameters .call (this,
                                                                      this .target,
                                                                      this ._textureProperties .getValue (),
                                                                      this .texturePropertiesNode,
                                                                      this .width,
                                                                      this .height,
                                                                      this ._repeatS .getValue (),
                                                                      this ._repeatT .getValue (),
                                                                      false);
   },
   setShaderUniforms (gl, channel)
   {
      const textureUnit = this .getBrowser () .popTextureUnit ();

      gl .activeTexture (gl .TEXTURE0 + textureUnit);
      gl .bindTexture (gl .TEXTURE_2D, this .getTexture ());
      gl .uniform1i (channel .texture2D, textureUnit);
   },
});

Object .defineProperties (X3DTexture2DNode, Core_X3DNode .getStaticProperties ("X3DTexture2DNode", "Texturing", 1));

const X3DTexture2DNode_default_ = X3DTexture2DNode;
;

/* harmony default export */ const Texturing_X3DTexture2DNode = (Namespace/* default */.A .add ("X3DTexture2DNode", X3DTexture2DNode_default_));
;// ./src/x_ite/Components/Texturing/ImageTexture.js
/* provided dependency */ var ImageTexture_$ = __webpack_require__(44).A;










function ImageTexture (executionContext)
{
   Texturing_X3DTexture2DNode .call (this, executionContext);
   Networking_X3DUrlObject     .call (this, executionContext);

   this .addType (Base_X3DConstants .ImageTexture);

   // Private properties

   this .image    = ImageTexture_$("<img></img>");
   this .urlStack = new x_ite_Fields .MFString ();

   this .getMatrix () .set ([1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1]); // flipY
}

Object .assign (Object .setPrototypeOf (ImageTexture .prototype, Texturing_X3DTexture2DNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Texturing_X3DTexture2DNode .prototype .initialize .call (this);
      Networking_X3DUrlObject     .prototype .initialize .call (this);

      this ._colorSpaceConversion .addInterest ("loadNow", this);

      this .image
         .on ("load", this .setImage .bind (this))
         .on ("abort error", this .setError .bind (this))
         .attr ("crossorigin", "anonymous");

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   getTextureType ()
   {
      return 1;
   },
   unloadData ()
   {
      this .clearTexture ();
   },
   loadData ()
   {
      this .urlStack .setValue (this ._url);
      this .loadNext ();
   },
   loadNext ()
   {
      if (this .urlStack .length === 0)
      {
         this .clearTexture ();
         this .updateOutputs (0, 0, 0);
         this .setLoadState (Base_X3DConstants .FAILED_STATE);
         return;
      }

      new InputOutput_FileLoader (this, { dataAsString: false }) .loadDocument ([this .urlStack .shift ()], (data, fileURL) =>
      {
         if (data === null)
         {
            this .loadNext ();
         }
         else if (data instanceof ArrayBuffer)
         {
            this .fileURL = new URL (fileURL);

            if (this .fileURL .pathname .match (/\.ktx2?(?:\.gz)?$/) || this .fileURL .href .match (/^\s*data:image\/ktx2[;,]/s))
            {
               this .setLinear (true);
               this .setMipMaps (false);

               this .getBrowser () .getKTXDecoder ()
                  .then (decoder => decoder .loadKTXFromBuffer (data))
                  .then (texture => this .setKTXTexture (texture))
                  .catch (error => this .setError ({ type: error .message }));
            }
            else
            {
               this .setLinear (false);
               this .setMipMaps (true);

               this .objectURL = URL .createObjectURL (new Blob ([data]));

               this .image .attr ("src", this .objectURL);
            }
         }
         else
         {
            throw new Error ("ImageTexture: no suitable file type handler found.");
         }
      });
   },
   setError (event)
   {
      if (this .fileURL .protocol !== "data:")
         console .warn (`Error loading image '${decodeURI (this .fileURL)}:'`, event .type);

      URL .revokeObjectURL (this .objectURL);

      this .loadNext ();
   },
   setKTXTexture (texture)
   {
      if (texture .target !== this .getTarget ())
         return this .setError ({ type: "Invalid KTX texture target, must be 'TEXTURE_2D'." });

      if (DEVELOPMENT)
      {
         if (this .fileURL .protocol !== "data:")
            console .info (`Done loading image texture '${decodeURI (this .fileURL)}'.`);
      }

      try
      {
         const { baseWidth, baseHeight, numComponents } = texture;

         this .setTexture (texture);
         this .setTransparent (false);
         this .setWidth (baseWidth);
         this .setHeight (baseHeight);
         this .updateTextureParameters ();
         this .updateOutputs (baseWidth, baseHeight, numComponents);

         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
      }
      catch (error)
      {
         // Catch security error from cross origin requests.
         this .setError ({ type: error .message });
      }
   },
   setImage ()
   {
      if (DEVELOPMENT)
      {
         if (this .fileURL .protocol !== "data:")
            console .info (`Done loading image texture '${decodeURI (this .fileURL)}'.`);
      }

      try
      {
         const
            image             = this .image [0],
            { width, height } = image;

         // Upload image to GPU.

         this .setTextureData (width, height, this ._colorSpaceConversion .getValue (), this .isTransparent (), image);
         this .setTransparent (this .isImageTransparent (this .getTextureData (this .getTexture (), width, height)));
         this .updateOutputs (width, height, this .isTransparent () ? 4 : 3);
         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
         this .addNodeEvent ();
      }
      catch (error)
      {
         // Catch security error from cross origin requests.
         this .setError ({ type: error .message });
      }
      finally
      {
         URL .revokeObjectURL (this .objectURL);
      }
   },
   updateOutputs (width, height, colorDepth)
   {
      this ._width      = width;
      this ._height     = height;
      this ._colorDepth = colorDepth;
   },
   dispose ()
   {
      Networking_X3DUrlObject     .prototype .dispose .call (this);
      Texturing_X3DTexture2DNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (ImageTexture,
{
   ... Core_X3DNode .getStaticProperties ("ImageTexture", "Texturing", 1, "texture", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorSpaceConversion", new x_ite_Fields .SFBool (true)), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "width",                new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "height",               new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "colorDepth",           new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatS",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatT",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "textureProperties",    new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const ImageTexture_default_ = ImageTexture;
;

/* harmony default export */ const Texturing_ImageTexture = (Namespace/* default */.A .add ("ImageTexture", ImageTexture_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/Background.js









function Background (executionContext)
{
   EnvironmentalEffects_X3DBackgroundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Background);
}

Object .assign (Object .setPrototypeOf (Background .prototype, EnvironmentalEffects_X3DBackgroundNode .prototype),
{
   initialize ()
   {
      EnvironmentalEffects_X3DBackgroundNode .prototype .initialize .call (this);

      const
         frontTexture      = new Texturing_ImageTexture (this .getExecutionContext ()),
         backTexture       = new Texturing_ImageTexture (this .getExecutionContext ()),
         leftTexture       = new Texturing_ImageTexture (this .getExecutionContext ()),
         rightTexture      = new Texturing_ImageTexture (this .getExecutionContext ()),
         topTexture        = new Texturing_ImageTexture (this .getExecutionContext ()),
         bottomTexture     = new Texturing_ImageTexture (this .getExecutionContext ()),
         textureProperties = this .getBrowser () .getBackgroundTextureProperties ();

      this ._frontUrl  .addFieldInterest (frontTexture  ._url);
      this ._backUrl   .addFieldInterest (backTexture   ._url);
      this ._leftUrl   .addFieldInterest (leftTexture   ._url);
      this ._rightUrl  .addFieldInterest (rightTexture  ._url);
      this ._topUrl    .addFieldInterest (topTexture    ._url);
      this ._bottomUrl .addFieldInterest (bottomTexture ._url);

      frontTexture  ._url = this ._frontUrl;
      backTexture   ._url = this ._backUrl;
      leftTexture   ._url = this ._leftUrl;
      rightTexture  ._url = this ._rightUrl;
      topTexture    ._url = this ._topUrl;
      bottomTexture ._url = this ._bottomUrl;

      frontTexture  ._textureProperties = textureProperties;
      backTexture   ._textureProperties = textureProperties;
      leftTexture   ._textureProperties = textureProperties;
      rightTexture  ._textureProperties = textureProperties;
      topTexture    ._textureProperties = textureProperties;
      bottomTexture ._textureProperties = textureProperties;

      frontTexture  .setup ();
      backTexture   .setup ();
      leftTexture   .setup ();
      rightTexture  .setup ();
      topTexture    .setup ();
      bottomTexture .setup ();

      this .set_texture__ (0, frontTexture);
      this .set_texture__ (1, backTexture);
      this .set_texture__ (2, leftTexture);
      this .set_texture__ (3, rightTexture);
      this .set_texture__ (4, topTexture);
      this .set_texture__ (5, bottomTexture);
   }
});

Object .defineProperties (Background,
{
   ... Core_X3DNode .getStaticProperties ("Background", "EnvironmentalEffects", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",     new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "frontUrl",     new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backUrl",      new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "leftUrl",      new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "rightUrl",     new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "topUrl",       new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "bottomUrl",    new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "skyAngle",     new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "skyColor",     new x_ite_Fields .MFColor (new Numbers_Color3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "groundAngle",  new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "groundColor",  new x_ite_Fields .MFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",     new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const Background_default_ = Background;
;

/* harmony default export */ const EnvironmentalEffects_Background = (Namespace/* default */.A .add ("Background", Background_default_));
;// ./src/x_ite/Components/Layering/X3DLayerNode.js

















function X3DLayerNode (executionContext, defaultViewpoint, groupNode)
{
   Core_X3DNode         .call (this, executionContext);
   Rendering_X3DRenderObject .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DLayerNode);

   this .addChildObjects (Base_X3DConstants .inputOutput, "hidden",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly,  "display", new x_ite_Fields .SFBool (true));

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.3)
      this .addAlias ("isPickable", this ._pickable);

   // Create main Group node.
   // This Group node is setuped in Layer or LayoutLayer.

   let groupNodes;

   if (executionContext .hasComponent ("Picking"))
   {
      groupNodes = executionContext .createNode ("PickableGroup", false);

      if (groupNodes)
      {
         this ._pickable   .addFieldInterest (groupNodes ._pickable);
         this ._objectType .addFieldInterest (groupNodes ._objectType);
      }
   }

   groupNodes ??= new Grouping_Group (executionContext);

   groupNodes ._children = [groupNode];
   groupNodes .setPrivate (true);

   // Private properties

   this .active     = false;
   this .layer0     = false;
   this .groupNodes = groupNodes;
   this .groupNode  = groupNode;

   this .defaultNavigationInfo = new Navigation_NavigationInfo (executionContext);
   this .defaultViewpoint      = defaultViewpoint;
   this .defaultBackground     = new EnvironmentalEffects_Background (executionContext);
   this .defaultFog            = new EnvironmentalEffects_Fog (executionContext);

   this .navigationInfoStack = new Execution_BindableStack (executionContext, this .defaultNavigationInfo);
   this .viewpointStack      = new Execution_BindableStack (executionContext, this .defaultViewpoint);
   this .backgroundStack     = new Execution_BindableStack (executionContext, this .defaultBackground);
   this .fogStack            = new Execution_BindableStack (executionContext, this .defaultFog);

   this .navigationInfos = new Execution_BindableList (executionContext, this .defaultNavigationInfo);
   this .viewpoints      = new Execution_BindableList (executionContext, this .defaultViewpoint);
   this .backgrounds     = new Execution_BindableList (executionContext, this .defaultBackground);
   this .fogs            = new Execution_BindableList (executionContext, this .defaultFog);

   this .defaultBackground .setHidden (true);
   this .defaultFog        .setHidden (true);
}

Object .assign (Object .setPrototypeOf (X3DLayerNode .prototype, Core_X3DNode .prototype),
   Rendering_X3DRenderObject .prototype,
{
   layer0: false,
   initialize ()
   {
      Core_X3DNode         .prototype .initialize .call (this);
      Rendering_X3DRenderObject .prototype .initialize .call (this);

      this ._hidden         .addInterest ("set_visible_and_hidden__", this);
      this ._visible        .addInterest ("set_visible_and_hidden__", this);
      this ._viewport       .addInterest ("set_viewport__",           this);
      this ._addChildren    .addInterest ("set_addChildren__",        this);
      this ._removeChildren .addInterest ("set_removeChildren__",     this);

      this ._children .addFieldInterest (this .groupNode ._children);
      this .groupNode ._children .addFieldInterest (this ._children);

      this .groupNode ._children = this ._children;

      this .groupNode .setPrivate (true);
      this .groupNode .setup ();
      this .groupNodes .setup ();

      this .defaultNavigationInfo .setup ();
      this .defaultViewpoint      .setup ();
      this .defaultBackground     .setup ();
      this .defaultFog            .setup ();

      this .navigationInfoStack .setup ();
      this .viewpointStack      .setup ();
      this .backgroundStack     .setup ();
      this .fogStack            .setup ();

      this .navigationInfos .setup ();
      this .viewpoints      .setup ();
      this .backgrounds     .setup ();
      this .fogs            .setup ();

      this .set_visible_and_hidden__ ();
      this .set_viewport__ ();
   },
   isHidden ()
   {
      return this ._hidden .getValue ();
   },
   setHidden (value)
   {
      if (value === this ._hidden .getValue ())
         return;

      this ._hidden = value;
   },
   getBBox (bbox, shadows)
   {
      return this .groupNodes .getBBox (bbox, shadows);
   },
   isLayer0 ()
   {
      return this .layer0;
   },
   setLayer0 (value)
   {
      this .layer0 = value;
      this .defaultBackground .setHidden (!value);
   },
   getLayer ()
   {
      return this;
   },
   getGroups ()
   {
      return this .groupNodes;
   },
   getGroup ()
   {
      return this .groupNode;
   },
   getViewport ()
   {
      return this .viewportNode;
   },
   getBackground ()
   {
      return this .backgroundStack .top ();
   },
   getFog ()
   {
      return this .fogStack .top ();
   },
   getNavigationInfo ()
   {
      return this .navigationInfoStack .top ();
   },
   getViewpoint ()
   {
      return this .viewpointStack .top ();
   },
   getBackgrounds ()
   {
      return this .backgrounds;
   },
   getFogs ()
   {
      return this .fogs;
   },
   getNavigationInfos ()
   {
      return this .navigationInfos;
   },
   getViewpoints ()
   {
      return this .viewpoints;
   },
   getUserViewpoints ()
   {
      const
         browser                = this .getBrowser (),
         enableInlineViewpoints = browser .getBrowserOption ("EnableInlineViewpoints"),
         currentScene           = browser .currentScene;

      return Array .from (new Set (this .viewpoints .get ()
         .filter (viewpointNode => viewpointNode .getDescriptions () .length)
         .filter (viewpointNode => enableInlineViewpoints || viewpointNode .getScene () === currentScene)));
   },
   getBackgroundStack ()
   {
      return this .backgroundStack;
   },
   getFogStack ()
   {
      return this .fogStack;
   },
   getNavigationInfoStack ()
   {
      return this .navigationInfoStack;
   },
   getViewpointStack ()
   {
      return this .viewpointStack;
   },
   viewAll (transitionTime = 1, factor = 1, straighten = false)
   {
      const
         viewpointNode = this .getViewpoint (),
         bbox          = this .getBBox (new Geometry_Box3 ()) .multRight (viewpointNode .getModelMatrix () .copy () .inverse ());

      if (bbox .size .equals (Numbers_Vector3 .ZERO))
         return;

      viewpointNode .lookAt (this, bbox .center, viewpointNode .getLookAtDistance (bbox), transitionTime, factor, straighten);
   },
   straightenView ()
   {
      this .getViewpoint () .straightenView (this);
   },
   set_visible_and_hidden__ ()
   {
      const value = this ._visible .getValue () && !this ._hidden .getValue ();

      if (value === this ._display .getValue ())
         return;

      this ._display = value;
   },
   set_viewport__ ()
   {
      this .viewportNode = Base_X3DCast (Base_X3DConstants .X3DViewportNode, this ._viewport)
         ?? this .getBrowser () .getDefaultViewport ();
   },
   set_addChildren__ ()
   {
      this ._addChildren .setTainted (true);

      this .groupNode ._addChildren = this ._addChildren;
      this ._addChildren .length    = 0;

      this ._addChildren .setTainted (false);
   },
   set_removeChildren__ ()
   {
      this ._removeChildren .setTainted (true);

      this .groupNode ._removeChildren = this ._removeChildren;
      this ._removeChildren .length    = 0;

      this ._removeChildren .setTainted (false);
   },
   bindBindables (viewpointName)
   {
      this .traverse (Rendering_TraverseType .CAMERA, this);

      // Bind first viewpoint in viewpoint list and other bindables too.

      const
         navigationInfoNode = this .navigationInfos .getBound (),
         backgroundNode     = this .backgrounds     .getBound (),
         fogNode            = this .fogs            .getBound (),
         viewpointNode      = this .viewpoints      .getBound (viewpointName);

      this .navigationInfoStack .pushOnTop (navigationInfoNode);
      this .backgroundStack     .pushOnTop (backgroundNode);
      this .fogStack            .pushOnTop (fogNode);
      this .viewpointStack      .pushOnTop (viewpointNode);

      // Update matrices of viewpoint.

      viewpointNode .resetUserOffsets ();

      if (viewpointNode ._viewAll .getValue ())
         viewpointNode .viewAll (this .getBBox (new Geometry_Box3 ()));

      viewpointNode .update ();
   },
   traverse: (() =>
   {
      const projectionMatrix = new Numbers_Matrix4 ();

      return function (type, renderObject = this)
      {
         const
            browser       = this .getBrowser (),
            viewpointNode = this .getViewpoint (),
            pose          = browser .getPose ();

         if (pose ?.views .length)
         {
            switch (type)
            {
               case Rendering_TraverseType .POINTER:
               {
                  const
                     navigationInfoNode = this .getNavigationInfo (),
                     farValue           = viewpointNode .getFarDistance (navigationInfoNode),
                     inputSource        = browser .getPointingInputSource ();

                  Geometry_Camera .ortho (-1, 1, -1, 1, 0, farValue, projectionMatrix);

                  this .getProjectionMatrix ()  .push (projectionMatrix);
                  this .getCameraSpaceMatrix () .push (inputSource .matrix);
                  this .getViewMatrix ()        .push (inputSource .inverse);

                  if (this !== browser .getActiveLayer ())
                  {
                     // Remove pose effect from matrices here.
                     this .getCameraSpaceMatrix () .multRight (pose .viewMatrix);
                     this .getViewMatrix ()        .multLeft  (pose .cameraSpaceMatrix);
                  }

                  this .getCameraSpaceMatrix () .multRight (viewpointNode .getCameraSpaceMatrix ());
                  this .getViewMatrix ()        .multLeft  (viewpointNode .getViewMatrix ());
                  break;
               }
               case Rendering_TraverseType .COLLISION:
               {
                  // This projection matrix will change later before rendering.
                  this .getProjectionMatrix ()  .push (pose .views [0] .projectionMatrix);
                  this .getCameraSpaceMatrix () .push (viewpointNode .getCameraSpaceMatrix ());
                  this .getViewMatrix ()        .push (viewpointNode .getViewMatrix ());
                  break;
               }
               default:
               {
                  // This projection matrix will change later before rendering.
                  this .getProjectionMatrix () .push (pose .views [0] .projectionMatrix);

                  if (this === browser .getActiveLayer ())
                  {
                     this .getCameraSpaceMatrix () .push (pose .cameraSpaceMatrix);
                     this .getViewMatrix ()        .push (pose .viewMatrix);

                     this .getCameraSpaceMatrix () .multRight (viewpointNode .getCameraSpaceMatrix ());
                     this .getViewMatrix ()        .multLeft  (viewpointNode .getViewMatrix ());
                  }
                  else
                  {
                     this .getCameraSpaceMatrix () .push (viewpointNode .getCameraSpaceMatrix ());
                     this .getViewMatrix ()        .push (viewpointNode .getViewMatrix ());
                  }

                  break;
               }
            }
         }
         else
         {
            this .getProjectionMatrix ()  .push (viewpointNode .getProjectionMatrix (this));
            this .getCameraSpaceMatrix () .push (viewpointNode .getCameraSpaceMatrix ());
            this .getViewMatrix ()        .push (viewpointNode .getViewMatrix ());
         }

         switch (type)
         {
            case Rendering_TraverseType .POINTER:
               this .pointer (type, renderObject);
               break;
            case Rendering_TraverseType .CAMERA:
               this .camera (type, renderObject);
               break;
            case Rendering_TraverseType .PICKING:
               this .picking (type, renderObject);
               break;
            case Rendering_TraverseType .COLLISION:
               this .collision (type, renderObject);
               break;
            case Rendering_TraverseType .DEPTH:
            case Rendering_TraverseType .SHADOW:
            case Rendering_TraverseType .DISPLAY:
               this .display (type, renderObject);
               break;
         }

         this .getViewMatrix ()        .pop ();
         this .getCameraSpaceMatrix () .pop ();
         this .getProjectionMatrix ()  .pop ();
      };
   })(),
   pointer (type, renderObject)
   {
      if (!this ._pointerEvents .getValue ())
         return;

      const
         browser  = this .getBrowser (),
         viewport = this .viewportNode .getRectangle ();

      if (browser .getPointingLayer ())
      {
         if (browser .getPointingLayer () !== this)
            return;
      }
      else
      {
         if (!browser .isPointerInRectangle (viewport))
            return;
      }

      const { viewportNode, groupNodes } = this;

      this .setHitRay (this .getProjectionMatrix () .get (), viewport, browser .getPointer ());
      this .getModelViewMatrix () .push (this .getViewMatrix () .get ());

      viewportNode .push (this);
      renderObject .render (type, groupNodes .traverse, groupNodes);
      viewportNode .pop (this);

      this .getModelViewMatrix () .pop ();
   },
   camera (type, renderObject)
   {
      this .getModelViewMatrix () .push (Numbers_Matrix4 .IDENTITY);

      const { viewportNode, groupNodes } = this;

      viewportNode .push (this);
      groupNodes .traverse (type, renderObject);
      viewportNode .pop (this);

      this .getModelViewMatrix () .pop ();

      this .navigationInfos .update (this, this .navigationInfoStack);
      this .viewpoints      .update (this, this .viewpointStack);
      this .backgrounds     .update (this, this .backgroundStack);
      this .fogs            .update (this, this .fogStack);

      this .getViewpoint () .update ();
   },
   picking (type, renderObject)
   {
      if (!this ._pickable .getValue ())
         return;

      const { viewportNode, groupNodes } = this;

      this .getModelViewMatrix () .push (Numbers_Matrix4 .IDENTITY);

      viewportNode .push (this);
      groupNodes .traverse (type, renderObject);
      viewportNode .pop (this);

      this .getModelViewMatrix () .pop ();
   },
   collision: (() =>
   {
      const projectionMatrix = new Numbers_Matrix4 ();

      return function (type, renderObject)
      {
         const navigationInfoNode = this .getNavigationInfo ();

         if (navigationInfoNode ._transitionActive .getValue ())
            return;

         const
            collisionRadius = navigationInfoNode .getCollisionRadius (),
            avatarHeight    = navigationInfoNode .getAvatarHeight (),
            size            = Math .max (collisionRadius * 2, avatarHeight * 2);

         const { viewportNode, groupNodes } = this;

         Geometry_Camera .ortho (-size, size, -size, size, -size, size, projectionMatrix);

         this .getProjectionMatrix () .push (projectionMatrix);
         this .getModelViewMatrix  () .push (this .getViewMatrix () .get ());

         // Render
         viewportNode .push (this);
         renderObject .render (type, groupNodes .traverse, groupNodes);
         viewportNode .pop (this);

         this .getModelViewMatrix  () .pop ();
         this .getProjectionMatrix () .pop ();
      };
   })(),
   display (type, renderObject)
   {
      const { viewportNode, groupNodes } = this;

      this .getNavigationInfo () .enable (type, renderObject);
      this .getModelViewMatrix () .push (this .getViewMatrix () .get ());

      viewportNode .push (this);
      renderObject .render (type, groupNodes .traverse, groupNodes);
      viewportNode .pop (this);

      this .getModelViewMatrix () .pop ();
   },
});

Object .defineProperties (X3DLayerNode, Core_X3DNode .getStaticProperties ("X3DLayerNode", "Layering", 1));

const X3DLayerNode_default_ = X3DLayerNode;
;

/* harmony default export */ const Layering_X3DLayerNode = (Namespace/* default */.A .add ("X3DLayerNode", X3DLayerNode_default_));
;// ./src/x_ite/Components/Core/X3DSensorNode.js




function X3DSensorNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSensorNode);
}

Object .setPrototypeOf (X3DSensorNode .prototype, Core_X3DChildNode .prototype);

Object .defineProperties (X3DSensorNode, Core_X3DNode .getStaticProperties ("X3DSensorNode", "Core", 1));

const X3DSensorNode_default_ = X3DSensorNode;
;

/* harmony default export */ const Core_X3DSensorNode = (Namespace/* default */.A .add ("X3DSensorNode", X3DSensorNode_default_));
;// ./src/x_ite/Components/Time/X3DTimeDependentNode.js





function X3DTimeDependentNode (executionContext)
{
   this .addType (Base_X3DConstants .X3DTimeDependentNode);

   this .addChildObjects (Base_X3DConstants .outputOnly,  "initialized", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .inputOutput, "evenLive",    new x_ite_Fields .SFBool ());

   this .startTimeValue  = 0;
   this .pauseTimeValue  = 0;
   this .resumeTimeValue = 0;
   this .stopTimeValue   = 0;
   this .start           = 0;
   this .pause           = 0;
   this .pauseInterval   = 0;
   this .timeouts        = new Map ();
   this .disabled        = false;
}

Object .assign (Object .setPrototypeOf (X3DTimeDependentNode .prototype, Core_X3DChildNode .prototype),
{
   initialize ()
   {
      this .getLive () .addInterest ("set_live__", this);
      this ._evenLive  .addInterest (Symbol .for ("X_ITE.X3DBaseNode.set_live__"), this);

      this ._initialized .addInterest ("set_loop__",       this);
      this ._enabled     .addInterest ("set_enabled__",    this);
      this ._loop        .addInterest ("set_loop__",       this);
      this ._startTime   .addInterest ("set_startTime__",  this);
      this ._pauseTime   .addInterest ("set_pauseTime__",  this);
      this ._resumeTime  .addInterest ("set_resumeTime__", this);
      this ._stopTime    .addInterest ("set_stopTime__",   this);

      this .startTimeValue  = this ._startTime  .getValue ();
      this .pauseTimeValue  = this ._pauseTime  .getValue ();
      this .resumeTimeValue = this ._resumeTime .getValue ();
      this .stopTimeValue   = this ._stopTime   .getValue ();

      this ._initialized = this .getBrowser () .getCurrentTime ();
   },
   checkLiveState ()
   {
      ///  Determines the live state of this node.

      return this .isLive () && (this .getExecutionContext () .getLive () .getValue () || this ._evenLive .getValue ());
   },
   getElapsedTime ()
   {
      return this .getBrowser () .getCurrentTime () - this .start - this .pauseInterval;
   },
   resetElapsedTime ()
   {
      this .start         = this .getBrowser () .getCurrentTime ();
      this .pause         = this .getBrowser () .getCurrentTime ();
      this .pauseInterval = 0;
   },
   set_live__ ()
   {
      if (this .getLive () .getValue ())
      {
         if (this .disabled)
         {
            this .disabled = false;

            if (this ._isActive .getValue () && !this ._isPaused .getValue ())
               this .real_resume ();
         }
      }
      else
      {
         if (!this .disabled && this ._isActive .getValue () && !this ._isPaused .getValue ())
         {
            // Only disable if needed, i.e. if running!
            this .disabled = true;
            this .real_pause ();
         }
      }
   },
   set_enabled__ ()
   {
      if (this ._enabled .getValue ())
         this .set_loop__ ();

      else
         this .stop ();
   },
   set_loop__ ()
   {
      if (this ._enabled .getValue ())
      {
         if (this ._loop .getValue ())
         {
            if (this .stopTimeValue <= this .startTimeValue)
            {
               if (this .startTimeValue <= this .getBrowser () .getCurrentTime ())
                  this .do_start ();
            }
         }
      }

      this .set_loop ();
   },
   set_startTime__ ()
   {
      this .startTimeValue = this ._startTime .getValue ();

      if (this ._enabled .getValue ())
      {
         this .removeTimeout ("start");

         if (this .startTimeValue <= this .getBrowser () .getCurrentTime ())
            this .do_start ();

         else
            this .addTimeout ("start", "do_start", this .startTimeValue);
      }
   },
   set_pauseTime__ ()
   {
      this .pauseTimeValue = this ._pauseTime .getValue ();

      if (this ._enabled .getValue ())
      {
         this .removeTimeout ("pause");

         if (this .pauseTimeValue <= this .resumeTimeValue)
            return;

         if (this .pauseTimeValue <= this .getBrowser () .getCurrentTime ())
            this .do_pause ();

         else
            this .addTimeout ("pause", "do_pause", this .pauseTimeValue);
      }
   },
   set_resumeTime__ ()
   {
      this .resumeTimeValue = this ._resumeTime .getValue ();

      if (this ._enabled .getValue ())
      {
         this .removeTimeout ("resume");

         if (this .resumeTimeValue <= this .pauseTimeValue)
            return;

         if (this .resumeTimeValue <= this .getBrowser () .getCurrentTime ())
            this .do_resume ();

         else
            this .addTimeout ("resume", "do_resume", this .resumeTimeValue);
      }
   },
   set_stopTime__ ()
   {
      this .stopTimeValue = this ._stopTime .getValue ();

      if (this ._enabled .getValue ())
      {
         this .removeTimeout ("stop");

         if (this .stopTimeValue <= this .startTimeValue)
            return;

         if (this .stopTimeValue <= this .getBrowser () .getCurrentTime ())
            this .do_stop ();

         else
            this .addTimeout ("stop", "do_stop", this .stopTimeValue);
      }
   },
   do_start ()
   {
      if (!this ._isActive .getValue ())
      {
         this .resetElapsedTime ();

         // The event order below is very important.

         this ._isActive    = true;
         this ._elapsedTime = 0;

         this .set_start ();

         if (this .getLive () .getValue ())
         {
            this .getBrowser () .timeEvents () .addInterest ("set_time" ,this);
         }
         else
         {
            this .disabled = true;
            this .real_pause ();
         }
      }
   },
   do_pause ()
   {
      if (this ._isActive .getValue () && !this ._isPaused .getValue ())
      {
         this ._isPaused = true;

         if (this .getLive () .getValue ())
            this .real_pause ();
      }
   },
   real_pause ()
   {
      this .pause = Date .now () / 1000;

      this .set_pause ();

      this .getBrowser () .timeEvents () .removeInterest ("set_time" ,this);
   },
   do_resume ()
   {
      if (this ._isActive .getValue () && this ._isPaused .getValue ())
      {
         this ._isPaused = false;

         if (this .getLive () .getValue ())
            this .real_resume ();
      }
   },
   real_resume ()
   {
      const interval = Date .now () / 1000 - this .pause;

      this .pauseInterval += interval;

      this .set_resume (interval);

      this .getBrowser () .timeEvents () .addInterest ("set_time", this);
      this .getBrowser () .addBrowserEvent ();
   },
   do_stop ()
   {
      this .stop ();
   },
   stop ()
   {
      if (this ._isActive .getValue ())
      {
         // The event order below is very important.

         this .set_stop ();

         this ._elapsedTime = this .getElapsedTime ();

         if (this ._isPaused .getValue ())
            this ._isPaused = false;

         this ._isActive = false;

         this .getBrowser () .timeEvents () .removeInterest ("set_time" ,this);
      }
   },
   addTimeout (name, callback, startTime)
   {
      const ms = (startTime - this .getBrowser () .getCurrentTime ()) * 1000;

      this .removeTimeout (name);

      this .timeouts .set (name, setTimeout (() => this .processTimeout (callback), ms));
   },
   removeTimeout (name)
   {
      clearTimeout (this .timeouts .get (name));

      this .timeouts .delete (name);
   },
   processTimeout (callback)
   {
      if (!this ._enabled .getValue ())
         return;

      if (!(this .getLive () .getValue ()))
         return;

      this .getBrowser () .advanceOnlyTime ();

      this [callback] ();
   },
   set_loop: Function .prototype,
   set_start: Function .prototype,
   set_pause: Function .prototype,
   set_resume: Function .prototype,
   set_stop: Function .prototype,
   set_time: Function .prototype,
   dispose ()
   {
      for (const name of [... this .timeouts .keys ()])
         this .removeTimeout (name);
   },
});

Object .defineProperties (X3DTimeDependentNode, Core_X3DNode .getStaticProperties ("X3DTimeDependentNode", "Time", 1));

const X3DTimeDependentNode_default_ = X3DTimeDependentNode;
;

/* harmony default export */ const Time_X3DTimeDependentNode = (Namespace/* default */.A .add ("X3DTimeDependentNode", X3DTimeDependentNode_default_));
;// ./src/x_ite/Components/Time/TimeSensor.js









function TimeSensor (executionContext)
{
   Core_X3DSensorNode        .call (this, executionContext);
   Time_X3DTimeDependentNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TimeSensor);

   this .addChildObjects (Base_X3DConstants .inputOutput, "range", new x_ite_Fields .MFFloat (0, 0, 1)); // current, first, last (in fractions) - play range starting at current

   this .cycle    = 0;
   this .interval = 0;
   this .fraction = 0;
   this .first    = 0;
   this .last     = 1;
   this .scale    = 1;
}

Object .assign (Object .setPrototypeOf (TimeSensor .prototype, Core_X3DSensorNode .prototype),
   Time_X3DTimeDependentNode .prototype,
{
   initialize ()
   {
      Core_X3DSensorNode        .prototype .initialize .call (this);
      Time_X3DTimeDependentNode .prototype .initialize .call (this);

      this ._cycleInterval .addInterest ("set_cycleInterval__", this);
      this ._range         .addInterest ("set_range__",         this);
   },
   setRange (fraction, firstFraction, lastFraction, offset)
   {
      const
         currentTime   = this .getBrowser () .getCurrentTime (),
         startTime     = this ._startTime .getValue (),
         cycleInterval = this ._cycleInterval .getValue ();

      this .first    = Math_Algorithm .clamp (firstFraction, 0, 1);
      this .last     = Math_Algorithm .clamp (lastFraction, 0, 1);
      this .scale    = this .last - this .first;
      this .interval = cycleInterval * this .scale;
      this .offset   = offset && this .interval ? (currentTime - startTime) / this .interval : 0;
      this .fraction = Math_Algorithm .fract (fraction + this .offset);
      this .cycle    = currentTime - (this .fraction - this .first) * cycleInterval;
   },
   set_cycleInterval__ ()
   {
      if (!this ._isActive .getValue ())
         return;

      this .setRange (this .fraction, this ._range [1], this ._range [2], false);
   },
   set_range__ ()
   {
      if (!this ._isActive .getValue ())
         return;

      this .setRange (this ._range [0], this ._range [1], this ._range [2], false);

      if (this ._isPaused .getValue ())
         return;

      this .set_fraction (this .getBrowser () .getCurrentTime ());
   },
   set_start ()
   {
      this .setRange (this ._range [0], this ._range [1], this ._range [2], true);

      const time = this .getBrowser () .getCurrentTime ();

      this ._time             = time;
      this ._cycleTime        = time;
      this ._fraction_changed = this .fraction;
   },
   set_resume (pauseInterval)
   {
      this .setRange (this .fraction, this ._range [1], this ._range [2], false);
   },
   set_fraction (time)
   {
      const fraction = this .first + (this .interval ? Math_Algorithm .fract ((time - this .cycle) / this .interval) : 0) * this .scale;

      this .fraction          = fraction;
      this ._fraction_changed = fraction;
   },
   set_time ()
   {
      // The event order below is very important.

      const time = this .getBrowser () .getCurrentTime ();

      this ._time = time;

      if (time - this .cycle >= this .interval)
      {
         if (this ._loop .getValue ())
         {
            if (this .interval)
            {
               this .cycle += this .interval * Math .floor ((time - this .cycle) / this .interval);

               this ._elapsedTime = this .getElapsedTime ();
               this ._cycleTime   = time;

               this .set_fraction (time);
            }
         }
         else
         {
            this ._elapsedTime      = this .getElapsedTime ();
            this ._fraction_changed = this .fraction = this .last;

            this .stop ();
         }
      }
      else
      {
         this ._elapsedTime = this .getElapsedTime ();

         this .set_fraction (time);
      }
   },
   dispose ()
   {
      Time_X3DTimeDependentNode .prototype .dispose .call (this);
      Core_X3DSensorNode        .prototype .dispose .call (this);
   },
});

Object .defineProperties (TimeSensor,
{
   ... Core_X3DNode .getStaticProperties ("TimeSensor", "Time", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "cycleInterval",    new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "loop",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",        new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",       new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",        new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",         new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "cycleTime",        new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",      new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "fraction_changed", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "time",             new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const TimeSensor_default_ = TimeSensor;
;

/* harmony default export */ const Time_TimeSensor = (Namespace/* default */.A .add ("TimeSensor", TimeSensor_default_));
;// ./src/x_ite/Components/Interpolation/X3DInterpolatorNode.js





function X3DInterpolatorNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DInterpolatorNode);
}

Object .assign (Object .setPrototypeOf (X3DInterpolatorNode .prototype, Core_X3DChildNode .prototype),
{
   setup ()
   {
      // If an X3DInterpolatorNode value_changed outputOnly field is read before it receives any inputs,
      // keyValue[0] is returned if keyValue is not empty. If keyValue is empty (i.e., [ ]), the initial
      // value for the respective field type is returned (EXAMPLE  (0, 0, 0) for Fields .SFVec3f);

      this .set_key__ ();
      this .set_fraction__ ();

      Core_X3DChildNode .prototype .setup .call (this);
   },
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this ._set_fraction .addInterest ("set_fraction__", this);
      this ._key          .addInterest ("set_key__",      this);
   },
   set_fraction__ ()
   {
      const
         key      = this ._key,
         length   = key .length,
         fraction = this ._set_fraction .getValue ();

      switch (length)
      {
         case 0:
            // Interpolator nodes containing no keys in the key field shall not produce any events.
            return;
         case 1:
            return this .interpolate (0, 0, 0);
         default:
         {
            if (fraction <= key [0])
               return this .interpolate (0, 1, 0);

            const index1 = Math_Algorithm .upperBound (key, 0, length, fraction);

            if (index1 !== length)
            {
               const
                  index0 = index1 - 1,
                  weight = (fraction - key [index0]) / (key [index1] - key [index0]);

               this .interpolate (index0, index1, Math_Algorithm .clamp (weight, 0, 1));
            }
            else
               this .interpolate (length - 2, length - 1, 1);
         }
      }
   },
   set_key__ ()
   {
      this .set_keyValue__ ();
   },
});

Object .defineProperties (X3DInterpolatorNode, Core_X3DNode .getStaticProperties ("X3DInterpolatorNode", "Interpolation", 1));

const X3DInterpolatorNode_default_ = X3DInterpolatorNode;
;

/* harmony default export */ const Interpolation_X3DInterpolatorNode = (Namespace/* default */.A .add ("X3DInterpolatorNode", X3DInterpolatorNode_default_));
;// ./src/x_ite/Components/Interpolation/EaseInEaseOut.js







function EaseInEaseOut (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .EaseInEaseOut);
}

Object .assign (Object .setPrototypeOf (EaseInEaseOut .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._easeInEaseOut .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      if (this ._easeInEaseOut .length < this ._key .length)
         this ._easeInEaseOut .resize (this ._key .length, this ._easeInEaseOut .length ? this ._easeInEaseOut [this ._easeInEaseOut .length - 1] : new x_ite_Fields .SFVec2f ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      let
         easeOut = this ._easeInEaseOut [index0] .y,
         easeIn  = this ._easeInEaseOut [index1] .x;

      const sum = easeOut + easeIn;

      if (sum < 0)
      {
         this ._modifiedFraction_changed = weight;
      }
      else
      {
         if (sum > 1)
         {
            easeIn  /= sum;
            easeOut /= sum;
         }

         const t = 1 / (2 - easeOut - easeIn);

         if (weight < easeOut)
         {
            this ._modifiedFraction_changed = (t / easeOut) * weight * weight;
         }
         else if (weight <= 1 - easeIn) // Spec says (weight < 1 - easeIn), but then we get a NaN below if easeIn == 0.
         {
            this ._modifiedFraction_changed = t * (2 * weight - easeOut);
         }
         else
         {
            const w = 1 - weight;

            this ._modifiedFraction_changed = 1 - ((t * w * w) / easeIn);
         }
      }
   },
});

Object .defineProperties (EaseInEaseOut,
{
   ... Core_X3DNode .getStaticProperties ("EaseInEaseOut", "Interpolation", 4, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",                 new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",             new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",                      new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "easeInEaseOut",            new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "modifiedFraction_changed", new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const EaseInEaseOut_default_ = EaseInEaseOut;
;

/* harmony default export */ const Interpolation_EaseInEaseOut = (Namespace/* default */.A .add ("EaseInEaseOut", EaseInEaseOut_default_));
;// ./src/x_ite/Components/Interpolation/PositionInterpolator.js








function PositionInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionInterpolator);
}

Object .assign (Object .setPrototypeOf (PositionInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFVec3f ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate: (() =>
   {
      const keyValue = new Numbers_Vector3 ();

      return function (index0, index1, weight)
      {
         this ._value_changed = keyValue .assign (this ._keyValue [index0] .getValue ()) .lerp (this ._keyValue [index1] .getValue (), weight);
      };
   })(),
});

Object .defineProperties (PositionInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("PositionInterpolator", "Interpolation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFVec3f ()),
      ]),
      enumerable: true,
   },
});

const PositionInterpolator_default_ = PositionInterpolator;
;

/* harmony default export */ const Interpolation_PositionInterpolator = (Namespace/* default */.A .add ("PositionInterpolator", PositionInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/OrientationInterpolator.js








function OrientationInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .OrientationInterpolator);

   // Units

   this ._keyValue      .setUnit ("angle");
   this ._value_changed .setUnit ("angle");
}

Object .assign (Object .setPrototypeOf (OrientationInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFRotation ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate: (() =>
   {
      const
         keyValue0 = new Numbers_Rotation4 (),
         keyValue1 = new Numbers_Rotation4 ();

      return function (index0, index1, weight)
      {
         // Both values can change in slerp.
         keyValue0 .assign (this ._keyValue [index0] .getValue ());
         keyValue1 .assign (this ._keyValue [index1] .getValue ());

         this ._value_changed = keyValue0 .slerp (keyValue1, weight);
      };
   })(),
});

Object .defineProperties (OrientationInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("OrientationInterpolator", "Interpolation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFRotation ()),
      ]),
      enumerable: true,
   },
});

const OrientationInterpolator_default_ = OrientationInterpolator;
;

/* harmony default export */ const Interpolation_OrientationInterpolator = (Namespace/* default */.A .add ("OrientationInterpolator", OrientationInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/ScalarInterpolator.js








function ScalarInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ScalarInterpolator);
}

Object .assign (Object .setPrototypeOf (ScalarInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : 0);

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      this ._value_changed = Math_Algorithm .lerp (this ._keyValue [index0], this ._keyValue [index1], weight);
   },
});

Object .defineProperties (ScalarInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("ScalarInterpolator", "Interpolation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const ScalarInterpolator_default_ = ScalarInterpolator;
;

/* harmony default export */ const Interpolation_ScalarInterpolator = (Namespace/* default */.A .add ("ScalarInterpolator", ScalarInterpolator_default_));
;// ./src/x_ite/Components/Navigation/X3DViewpointNode.js
















function X3DViewpointNode (executionContext)
{
   Core_X3DBindableNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DViewpointNode);

   this .addChildObjects (Base_X3DConstants .inputOutput, "positionOffset",         new x_ite_Fields .SFVec3f (),
                          Base_X3DConstants .inputOutput, "orientationOffset",      new x_ite_Fields .SFRotation (),
                          Base_X3DConstants .inputOutput, "scaleOffset",            new x_ite_Fields .SFVec3f (1, 1, 1),
                          Base_X3DConstants .inputOutput, "scaleOrientationOffset", new x_ite_Fields .SFRotation (),
                          Base_X3DConstants .inputOutput, "centerOfRotationOffset", new x_ite_Fields .SFVec3f (),
                          Base_X3DConstants .inputOutput, "fieldOfViewScale",       new x_ite_Fields .SFFloat (1));

   // Units

   this ._nearDistance .setUnit ("length");
   this ._farDistance  .setUnit ("length");

   // Private properties

   this .descriptions         = [ ];
   this .userPosition         = new Numbers_Vector3 ();
   this .userOrientation      = new Numbers_Rotation4 ();
   this .userCenterOfRotation = new Numbers_Vector3 ();
   this .modelMatrix          = new Numbers_Matrix4 ();
   this .cameraSpaceMatrix    = new Numbers_Matrix4 (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,  10, 1);
   this .viewMatrix           = new Numbers_Matrix4 (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -10, 1);

   const
      browser      = this .getBrowser (),
      privateScene = browser .getPrivateScene ();

   this .timeSensor                   = new Time_TimeSensor              (privateScene);
   this .easeInEaseOut                = new Interpolation_EaseInEaseOut           (privateScene);
   this .positionInterpolator         = new Interpolation_PositionInterpolator    (privateScene);
   this .orientationInterpolator      = new Interpolation_OrientationInterpolator (privateScene);
   this .scaleInterpolator            = new Interpolation_PositionInterpolator    (privateScene);
   this .scaleOrientationInterpolator = new Interpolation_OrientationInterpolator (privateScene);
   this .fieldOfViewScaleInterpolator = new Interpolation_ScalarInterpolator      (privateScene);
}

Object .assign (Object .setPrototypeOf (X3DViewpointNode .prototype, Core_X3DBindableNode .prototype),
{
   initialize ()
   {
      Core_X3DBindableNode .prototype .initialize .call (this);

      this .timeSensor ._stopTime = 1;
      this .timeSensor .setup ();

      this .easeInEaseOut ._key           = new x_ite_Fields .MFFloat (0, 1);
      this .easeInEaseOut ._easeInEaseOut = new x_ite_Fields .MFVec2f (new x_ite_Fields .SFVec2f (), new x_ite_Fields .SFVec2f ());
      this .easeInEaseOut .setup ();

      this .positionInterpolator         ._key = new x_ite_Fields .MFFloat (0, 1);
      this .orientationInterpolator      ._key = new x_ite_Fields .MFFloat (0, 1);
      this .scaleInterpolator            ._key = new x_ite_Fields .MFFloat (0, 1);
      this .scaleOrientationInterpolator ._key = new x_ite_Fields .MFFloat (0, 1);
      this .fieldOfViewScaleInterpolator ._key = new x_ite_Fields .MFFloat (0, 1);

      this .positionInterpolator         .setup ();
      this .orientationInterpolator      .setup ();
      this .scaleInterpolator            .setup ();
      this .scaleOrientationInterpolator .setup ();
      this .fieldOfViewScaleInterpolator .setup ();

      this .timeSensor ._fraction_changed .addFieldInterest (this .easeInEaseOut ._set_fraction);

      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .positionInterpolator         ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .orientationInterpolator      ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .scaleInterpolator            ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .scaleOrientationInterpolator ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .fieldOfViewScaleInterpolator ._set_fraction);

      this .positionInterpolator         ._value_changed .addFieldInterest (this ._positionOffset);
      this .orientationInterpolator      ._value_changed .addFieldInterest (this ._orientationOffset);
      this .scaleInterpolator            ._value_changed .addFieldInterest (this ._scaleOffset);
      this .scaleOrientationInterpolator ._value_changed .addFieldInterest (this ._scaleOrientationOffset);
      this .fieldOfViewScaleInterpolator ._value_changed .addFieldInterest (this ._fieldOfViewScale);

      this ._nearDistance   .addInterest ("set_nearDistance__",   this);
      this ._farDistance    .addInterest ("set_farDistance__",    this);
      this ._viewAll        .addInterest ("set_viewAll__",        this);
      this ._navigationInfo .addInterest ("set_navigationInfo__", this);
      this ._isBound        .addInterest ("set_bound__",          this);

      this .set_nearDistance__ ();
      this .set_farDistance__ ();
      this .set_navigationInfo__ ();
   },
   set_nearDistance__ ()
   {
      const nearDistance = this ._nearDistance .getValue ();

      this .nearDistance = nearDistance >= 0 ? nearDistance : undefined;
   },
   set_farDistance__ ()
   {
      const farDistance = this ._farDistance .getValue ();

      this .farDistance = farDistance >= 0 ? farDistance : undefined;
   },
   set_viewAll__ ()
   {
      if (!this ._viewAll .getValue ())
         return;

      if (!this ._isBound .getValue ())
         return;

      this ._set_bind = true;
   },
   set_navigationInfo__ ()
   {
      if (this .navigationInfoNode)
         this ._isBound .removeFieldInterest (this .navigationInfoNode ._set_bind);

      this .navigationInfoNode = Base_X3DCast (Base_X3DConstants .NavigationInfo, this ._navigationInfo);

      if (this .navigationInfoNode)
         this ._isBound .addFieldInterest (this .navigationInfoNode ._set_bind);
   },
   set_bound__ ()
   {
      if (this ._isBound .getValue ())
         this .getBrowser () .setDescription (this ._description);
      else
         this .timeSensor ._stopTime = Date .now () / 1000;
   },
   set_active__ (navigationInfoNode, active)
   {
      if (active .getValue ())
         return;

      this .timeSensor ._isActive .removeInterest ("set_active__", this);

      if (!this ._isBound .getValue ())
         return;

      if (this .timeSensor ._fraction_changed .getValue () !== 1)
         return;

      navigationInfoNode ._transitionComplete = true;
   },
   setInterpolators ()
   { },
   getDescriptions ()
   {
      return this .descriptions;
   },
   getPosition ()
   {
      return this ._position .getValue ();
   },
   setPosition (value)
   {
      this ._position = value;
   },
   getOrientation ()
   {
      return this ._orientation .getValue ();
   },
   setOrientation (value)
   {
      this ._orientation = value;
   },
   getCenterOfRotation ()
   {
      return this ._centerOfRotation .getValue ();
   },
   setCenterOfRotation (value)
   {
      this ._centerOfRotation = value;
   },
   getUserPosition ()
   {
      return this .userPosition .assign (this .getPosition ())
         .add (this ._positionOffset .getValue ());
   },
   setUserPosition (userPosition)
   {
      this ._positionOffset = this .userPosition .assign (userPosition)
         .subtract (this .getPosition ());
   },
   getUserOrientation ()
   {
      return this .userOrientation .assign (this .getOrientation ())
         .multRight (this ._orientationOffset .getValue ());
   },
   setUserOrientation (userOrientation)
   {
      this ._orientationOffset = this .userOrientation .assign (this .getOrientation ()) .inverse ()
         .multRight (userOrientation);
   },
   getUserCenterOfRotation ()
   {
      return this .userCenterOfRotation .assign (this .getCenterOfRotation ())
         .add (this ._centerOfRotationOffset .getValue ());
   },
   setUserCenterOfRotation (userCenterOfRotation)
   {
      this ._centerOfRotationOffset = this .userCenterOfRotation .assign (userCenterOfRotation)
         .subtract (this .getCenterOfRotation ());
   },
   getFieldOfViewScale ()
   {
      return this ._fieldOfViewScale .getValue ();
   },
   setFieldOfViewScale (value)
   {
      this ._fieldOfViewScale = value;
   },
   getNearDistance (navigationInfoNode)
   {
      return this .nearDistance ?? navigationInfoNode ?.getNearValue ();
   },
   setNearDistance (value)
   {
      this .nearDistance = value;
   },
   getFarDistance (navigationInfoNode)
   {
      return this .farDistance
         ?? (navigationInfoNode ? navigationInfoNode .getFarValue () || this .getMaxFarValue () : undefined);
   },
   setFarDistance (value)
   {
      this .farDistance = value;
   },
   getProjectionMatrix (renderObject, viewport)
   {
      const navigationInfoNode = renderObject .getNavigationInfo ();

      return this .getProjectionMatrixWithLimits (this .getNearDistance (navigationInfoNode),
                                                  this .getFarDistance (navigationInfoNode),
                                                  viewport ?? renderObject .getLayer () .getViewport () .getRectangle ());
   },
   getCameraSpaceMatrix ()
   {
      return this .cameraSpaceMatrix;
   },
   getViewMatrix ()
   {
      return this .viewMatrix;
   },
   getModelMatrix ()
   {
      return this .modelMatrix;
   },
   getMaxFarValue ()
   {
      return this .getBrowser () .getRenderingProperty ("LogarithmicDepthBuffer") ? 1e10 : 1e5;
   },
   getUpVector ()
   {
      // Local y-axis,
      // see https://www.web3d.org/documents/specifications/19775-1/V4.0/Part01/components/navigation.html#NavigationInfo.
      return Numbers_Vector3 .Y_AXIS;
   },
   getSpeedFactor ()
   {
      return 1;
   },
   setVRMLTransition (value)
   {
      // VRML behavior support.
      this .VRMLTransition = value;
   },
   getVRMLTransition ()
   {
      // VRML behavior support.
      return this .VRMLTransition;
   },
   checkTransition (description)
   {
      if (this .timeSensor ._isActive .getValue () && this .timeSensor ._description .getValue () === description)
         return true;

      this .timeSensor ._description = description;

      return false;
   },
   transitionStart (layerNode, fromViewpointNode)
   {
      if (this ._jump .getValue ())
      {
         if (this .checkTransition ("transitionStart"))
            return;

         const relative = this .getRelativeTransformation (fromViewpointNode);

         if (!this ._retainUserOffsets .getValue ())
            this .resetUserOffsets ();

         if (this ._viewAll .getValue ())
            this .viewAll (layerNode .getBBox (new Geometry_Box3 ()));

         // Handle NavigationInfo.

         const
            navigationInfoNode = layerNode .getNavigationInfo (),
            transitionTime     = navigationInfoNode ._transitionTime .getValue ();

         let transitionType = navigationInfoNode .getTransitionType ();

         // VRML behavior

         if (this .getExecutionContext () .getSpecificationVersion () == 2.0)
         {
            if (this .getVRMLTransition ())
               transitionType = "LINEAR";
            else
               transitionType = "TELEPORT";
         }

         this .setVRMLTransition (false);

         // End VRML behavior

         if (transitionTime <= 0)
            transitionType = "TELEPORT";

         if (this .constructor !== fromViewpointNode .constructor)
            transitionType = "TELEPORT";

         switch (transitionType)
         {
            case "TELEPORT":
            {
               navigationInfoNode ._transitionComplete = true;
               return;
            }
            case "ANIMATE":
            {
               this .easeInEaseOut ._easeInEaseOut = new x_ite_Fields .MFVec2f (new x_ite_Fields .SFVec2f (0, 1), new x_ite_Fields .SFVec2f (1, 0));
               break;
            }
            default:
            {
               // LINEAR
               this .easeInEaseOut ._easeInEaseOut = new x_ite_Fields .MFVec2f (new x_ite_Fields .SFVec2f (), new x_ite_Fields .SFVec2f ());
               break;
            }
         }

         navigationInfoNode ._transitionStart = true;

         this .timeSensor ._cycleInterval = transitionTime;
         this .timeSensor ._stopTime      = Date .now () / 1000;
         this .timeSensor ._startTime     = Date .now () / 1000;

         this .timeSensor ._isActive .addInterest ("set_active__", this, navigationInfoNode);

         this .positionInterpolator         ._keyValue = new x_ite_Fields .MFVec3f    (relative .position,         this ._positionOffset);
         this .orientationInterpolator      ._keyValue = new x_ite_Fields .MFRotation (relative .orientation,      this ._orientationOffset);
         this .scaleInterpolator            ._keyValue = new x_ite_Fields .MFVec3f    (relative .scale,            this ._scaleOffset);
         this .scaleOrientationInterpolator ._keyValue = new x_ite_Fields .MFRotation (relative .scaleOrientation, this ._scaleOrientationOffset);

         this ._positionOffset         = relative .position;
         this ._orientationOffset      = relative .orientation;
         this ._scaleOffset            = relative .scale;
         this ._scaleOrientationOffset = relative .scaleOrientation;

         this .setInterpolators (fromViewpointNode, relative);
      }
      else
      {
         this .transitionStop ();

         const navigationInfoNode = layerNode .getNavigationInfo ();

         navigationInfoNode ._transitionComplete = true;

         const relative = this .getRelativeTransformation (fromViewpointNode);

         this ._positionOffset         = relative .position;
         this ._orientationOffset      = relative .orientation;
         this ._scaleOffset            = relative .scale;
         this ._scaleOrientationOffset = relative .scaleOrientation;

         this .setInterpolators (fromViewpointNode, relative);
      }
   },
   transitionStop ()
   {
      this .timeSensor ._stopTime = Date .now () / 1000;
      this .timeSensor ._isActive .removeInterest ("set_active__", this);
   },
   resetUserOffsets ()
   {
      this ._positionOffset         = Numbers_Vector3   .ZERO;
      this ._orientationOffset      = Numbers_Rotation4 .IDENTITY;
      this ._scaleOffset            = Numbers_Vector3   .ONE;
      this ._scaleOrientationOffset = Numbers_Rotation4 .IDENTITY;
      this ._centerOfRotationOffset = Numbers_Vector3   .ZERO;
      this ._fieldOfViewScale       = 1;

      this .set_nearDistance__ ();
      this .set_farDistance__ ();
   },
   getRelativeTransformation: (() =>
   {
      const
         position         = new Numbers_Vector3 (),
         orientation      = new Numbers_Rotation4 (),
         scale            = new Numbers_Vector3 (),
         scaleOrientation = new Numbers_Rotation4 ();

      return function (fromViewpointNode)
      {
         const differenceMatrix = this .modelMatrix .copy () .multRight (fromViewpointNode .getViewMatrix ()) .inverse ();

         differenceMatrix .getTransform (position, orientation, scale, scaleOrientation);

         position .subtract (this .getPosition ());
         orientation .multLeft (this .getOrientation () .copy () .inverse ());

         return {
            position: position,
            orientation: orientation,
            scale: scale,
            scaleOrientation: scaleOrientation,
         };
      };
   })(),
   getLookAtRotation: (() =>
   {
      const
         x = new Numbers_Vector3 (),
         y = new Numbers_Vector3 (),
         z = new Numbers_Vector3 (),
         m = new Numbers_Matrix3 (),
         r = new Numbers_Rotation4 ();

      return function (fromPoint, toPoint)
      {
         const up = this .getUpVector (true);

         z .assign (fromPoint) .subtract (toPoint) .normalize ();
         x .assign (up) .cross (z) .normalize ();
         y .assign (z) .cross (x) .normalize ();

         m .set (... x, ... y, ... z);
         r .setMatrix (m);

         return r;
      };
   })(),
   lookAtPoint (layerNode, point, transitionTime = 1, factor = 1, straighten = false)
   {
      this .getCameraSpaceMatrix () .multVecMatrix (point);
      this .getModelMatrix () .copy () .inverse () .multVecMatrix (point);

      this .lookAt (layerNode, point, 0.5, transitionTime, factor, straighten);
   },
   lookAtBBox (layerNode, bbox, transitionTime = 1, factor = 1, straighten = false)
   {
      if (bbox .size .equals (Numbers_Vector3 .ZERO))
         return;

      bbox = bbox .copy () .multRight (this .getModelMatrix () .copy () .inverse ());

      this .lookAt (layerNode, bbox .center, this .getLookAtDistance (bbox), transitionTime, factor, straighten);
   },
   lookAt (layerNode, point, distance, transitionTime = 1, factor = 1, straighten = false)
   {
      this .timeSensor ._description = "lookAt";

      const
         offset = point .copy () .add (this .getUserOrientation () .multVecRot (new Numbers_Vector3 (0, 0, distance))) .subtract (this .getPosition ());

      layerNode .getNavigationInfo () ._transitionStart = true;

      this .timeSensor ._cycleInterval = transitionTime;
      this .timeSensor ._stopTime      = Date .now () / 1000;
      this .timeSensor ._startTime     = Date .now () / 1000;

      this .timeSensor ._isActive .addInterest ("set_active__", this, layerNode .getNavigationInfo ());

      this .easeInEaseOut ._easeInEaseOut = new x_ite_Fields .MFVec2f (new x_ite_Fields .SFVec2f (0, 1), new x_ite_Fields .SFVec2f (1, 0));

      const
         translation = this ._positionOffset .getValue () .copy () .lerp (offset, factor),
         direction   = this .getPosition () .copy () .add (translation) .subtract (point);

      let rotation = this ._orientationOffset .getValue () .copy () .multRight (Numbers_Rotation4 .fromVectors (this .getUserOrientation () .multVecRot (new Numbers_Vector3 (0, 0, 1)), direction));

      if (straighten)
      {
         rotation = this .getOrientation () .copy () .inverse () .multRight (this .straightenHorizon (this .getOrientation () .copy () .multRight (rotation)));
      }

      this .positionInterpolator         ._keyValue = new x_ite_Fields .MFVec3f (this ._positionOffset, translation);
      this .orientationInterpolator      ._keyValue = new x_ite_Fields .MFRotation (this ._orientationOffset, rotation);
      this .scaleInterpolator            ._keyValue = new x_ite_Fields .MFVec3f (this ._scaleOffset, Numbers_Vector3 .ONE);
      this .scaleOrientationInterpolator ._keyValue = new x_ite_Fields .MFRotation (this ._scaleOrientationOffset, this ._scaleOrientationOffset);

      const relative = this .getRelativeTransformation (this);

      this ._fieldOfViewScale       = 1;
      this ._centerOfRotationOffset = point .copy () .subtract (this .getCenterOfRotation ());
      this .nearDistance            = distance * (0.125 / 10);
      this .farDistance             = this .nearDistance * this .getMaxFarValue () / 0.125;

      this .setInterpolators (this, relative);
   },
   straightenView (layerNode)
   {
      if (this .checkTransition ("straightenView"))
         return;

      layerNode .getNavigationInfo () ._transitionStart = true;

      this .timeSensor ._cycleInterval = 1;
      this .timeSensor ._stopTime      = Date .now () / 1000;
      this .timeSensor ._startTime     = Date .now () / 1000;

      this .timeSensor ._isActive .addInterest ("set_active__", this, layerNode .getNavigationInfo ());

      this .easeInEaseOut ._easeInEaseOut = new x_ite_Fields .MFVec2f (new x_ite_Fields .SFVec2f (0, 1), new x_ite_Fields .SFVec2f (1, 0));

      const rotation = this .getOrientation () .copy () .inverse () .multRight (this .straightenHorizon (this .getUserOrientation ()));

      this .positionInterpolator         ._keyValue = new x_ite_Fields .MFVec3f (this ._positionOffset, this ._positionOffset);
      this .orientationInterpolator      ._keyValue = new x_ite_Fields .MFRotation (this ._orientationOffset, rotation);
      this .scaleInterpolator            ._keyValue = new x_ite_Fields .MFVec3f (this ._scaleOffset, this ._scaleOffset);
      this .scaleOrientationInterpolator ._keyValue = new x_ite_Fields .MFRotation (this ._scaleOrientationOffset, this ._scaleOrientationOffset);

      const relative = this .getRelativeTransformation (this);

      this ._fieldOfViewScale = 1;

      this .setInterpolators (this, relative);
   },
   straightenHorizon (orientation, upVector = this .getUpVector (true))
   {
      return orientation .straighten (upVector);
   },
   viewAll (bbox)
   {
      bbox .copy () .multRight (this .modelMatrix .copy () .inverse ());

      if (bbox .size .equals (Numbers_Vector3 .ZERO))
      {
         this .set_nearDistance__ ();
         this .set_farDistance__ ();
      }
      else
      {
         const
            direction       = this .getUserPosition () .copy () .subtract (bbox .center) .normalize (),
            distance        = this .getLookAtDistance (bbox),
            userPosition    = bbox .center .copy () .add (direction .multiply (distance)),
            userOrientation = this .getLookAtRotation (userPosition, bbox .center);

         this ._positionOffset         = userPosition .subtract (this .getPosition ());
         this ._orientationOffset      = this .getOrientation () .copy () .inverse () .multRight (userOrientation);
         this ._centerOfRotationOffset = bbox .center .copy () .subtract (this .getCenterOfRotation ());
         this ._fieldOfViewScale       = 1;
         this .nearDistance            = distance * (0.125 / 10);
         this .farDistance             = this .nearDistance * this .getMaxFarValue () / 0.125;
      }
   },
   traverse (type, renderObject)
   {
      // Handle NavigationInfo node.

      this .navigationInfoNode ?.traverse (type, renderObject);

      // Check if the viewpoint is displayed.

      this .descriptions .length = 0;

      const description = this ._description .getValue ();

      if (description)
      {
         if (renderObject .getViewpointGroups () .every (viewpointGroupNode => viewpointGroupNode .getDisplayed ()))
         {
            for (const viewpointGroupNode of renderObject .getViewpointGroups ())
            {
               const description = viewpointGroupNode ._description .getValue ();

               if (description)
                  this .descriptions .push (description);
            }

            this .descriptions .push (description);
         }
      }

      // Add the viewpoint to the list of available viewpoints.

      renderObject .getLayer () .getViewpoints () .push (this);

      this .modelMatrix .assign (renderObject .getModelViewMatrix () .get ());
   },
   update ()
   {
      this .cameraSpaceMatrix .setTransform (this .getUserPosition (),
                                             this .getUserOrientation (),
                                             this ._scaleOffset .getValue (),
                                             this ._scaleOrientationOffset .getValue ());

      this .cameraSpaceMatrix .multRight (this .modelMatrix);

      this .viewMatrix .assign (this .cameraSpaceMatrix) .inverse ();
   }
});

Object .defineProperties (X3DViewpointNode, Core_X3DNode .getStaticProperties ("X3DViewpointNode", "Navigation", 1));

const X3DViewpointNode_default_ = X3DViewpointNode;
;

/* harmony default export */ const Navigation_X3DViewpointNode = (Namespace/* default */.A .add ("X3DViewpointNode", X3DViewpointNode_default_));
;// ./src/x_ite/Components/Navigation/Viewpoint.js










function Viewpoint (executionContext)
{
   Navigation_X3DViewpointNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Viewpoint);

   // Units

   this ._position         .setUnit ("length");
   this ._centerOfRotation .setUnit ("length");
   this ._fieldOfView      .setUnit ("angle");

   // Private properties

   this .projectionMatrix = new Numbers_Matrix4 ();
}

Object .assign (Object .setPrototypeOf (Viewpoint .prototype, Navigation_X3DViewpointNode .prototype),
{
   getRelativeTransformation (fromViewpointNode)
   {
      const relative = Navigation_X3DViewpointNode .prototype .getRelativeTransformation .call (this, fromViewpointNode);

      if (fromViewpointNode .constructor === this .constructor)
         relative .fieldOfView = fromViewpointNode .getUserFieldOfView ();

      return relative;
   },
   setInterpolators (fromViewpointNode, relative)
   {
      if (fromViewpointNode .constructor === this .constructor)
      {
         const scale = relative .fieldOfView / this .getUserFieldOfView ();

         this .fieldOfViewScaleInterpolator ._keyValue = new x_ite_Fields .MFFloat (scale, this ._fieldOfViewScale .getValue ());

         this ._fieldOfViewScale = scale;
      }
      else
      {
         this .fieldOfViewScaleInterpolator ._keyValue = new x_ite_Fields .MFFloat (this ._fieldOfViewScale .getValue (), this ._fieldOfViewScale .getValue ());

         this ._fieldOfViewScale = this ._fieldOfViewScale .getValue ();
      }
   },
   getLogarithmicDepthBuffer ()
   {
      return false;
   },
   getFieldOfView ()
   {
      const fov = this ._fieldOfView .getValue ();

      return fov > 0 && fov < Math .PI ? fov : Math .PI / 4;
   },
   setFieldOfView (value)
   {
      this ._fieldOfView = value;
   },
   getUserFieldOfView ()
   {
      const fov = this ._fieldOfView .getValue () * this ._fieldOfViewScale .getValue ();

      return fov > 0 && fov < Math .PI ? fov : Math .PI / 4;
   },
   getScreenScale (point, viewport, screenScale)
   {
      // Returns the screen scale in meter/pixel for on pixel.

      const
         width  = viewport [2],
         height = viewport [3],
         pose   = this .getBrowser () .getPose ();

      // MDN says fov can be determined from projectionMatrix.
      // https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Perspective
      const fov1_2 = pose
         ? 1 / pose .views [0] .projectionMatrix [5]
         : Math .tan (this .getUserFieldOfView () / 2);

      let size = Math .abs (point .z) * fov1_2 * 2;

      if (width > height)
         size /= height;
      else
         size /= width;

      return screenScale .set (size, size, size);
   },
   getViewportSize: (() =>
   {
      const viewportSize = new Numbers_Vector2 ();

      return function (viewport, nearValue)
      {
         // Returns viewport size in meters.

         const
            width  = viewport [2],
            height = viewport [3],
            size   = nearValue * Math .tan (this .getUserFieldOfView () / 2) * 2,
            aspect = width / height;

         if (aspect > 1)
            return viewportSize .set (size * aspect, size);

         return viewportSize .set (size, size / aspect);
      };
   })(),
   getLookAtDistance (bbox)
   {
      return (bbox .size .norm () / 2) / Math .tan (this .getUserFieldOfView () / 2);
   },
   getProjectionMatrixWithLimits (nearValue, farValue, viewport)
   {
      return Geometry_Camera .perspective (this .getUserFieldOfView (), nearValue, farValue, viewport [2], viewport [3], this .projectionMatrix);
   },
});

Object .defineProperties (Viewpoint,
{
   ... Core_X3DNode .getStaticProperties ("Viewpoint", "Navigation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "position",          new x_ite_Fields .SFVec3f (0, 0, 10)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "orientation",       new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "centerOfRotation",  new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fieldOfView",       new x_ite_Fields .SFFloat (0.785398)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "nearDistance",      new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "farDistance",       new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "viewAll",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "jump",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "retainUserOffsets", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",          new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "navigationInfo",    new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const Viewpoint_default_ = Viewpoint;
;

/* harmony default export */ const Navigation_Viewpoint = (Namespace/* default */.A .add ("Viewpoint", Viewpoint_default_));
;// ./src/x_ite/Components/Layering/Layer.js









function Layer (executionContext)
{
   Layering_X3DLayerNode .call (this,
                       executionContext,
                       new Navigation_Viewpoint (executionContext),
                       new Grouping_Group (executionContext));

   this .addType (Base_X3DConstants .Layer);
}

Object .setPrototypeOf (Layer .prototype, Layering_X3DLayerNode .prototype);

Object .defineProperties (Layer,
{
   ... Core_X3DNode .getStaticProperties ("Layer", "Layering", 1, "layers", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pickable",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "objectType",     new x_ite_Fields .MFString ("ALL")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pointerEvents",  new x_ite_Fields .SFBool (true)), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "viewport",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Layer_default_ = Layer;
;

/* harmony default export */ const Layering_Layer = (Namespace/* default */.A .add ("Layer", Layer_default_));
;// ./src/x_ite/Parser/GLTF2Parser.js
/* provided dependency */ var GLTF2Parser_$ = __webpack_require__(44).A;


















// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
// https://github.com/KhronosGroup/glTF-Sample-Assets

const SAMPLES_PER_SECOND = 30; // in 1/s

function GLTF2Parser (scene)
{
   Parser_X3DParser    .call (this, scene);
   Parser_X3DOptimizer .call (this);

   // Optimizer

   this .removeEmptyGroups     = true;
   this .combineGroupingNodes  = true;
   this .optimizeInterpolators = true;

   // Globals

   this .extensions            = new Set ();
   this .envLights             = [ ];
   this .lights                = [ ];
   this .materialVariants      = [ ];
   this .materialVariantNodes  = [ ];
   this .buffers               = [ ];
   this .bufferViews           = [ ];
   this .accessors             = [ ];
   this .samplers              = [ ];
   this .textureCache          = new Map ();
   this .materials             = [ ];
   this .textureTransformNodes = [ ];
   this .meshes                = [ ];
   this .cameras               = [ ];
   this .nodes                 = [ ];
   this .skins                 = [ ];
   this .skeletons             = new Map ();
   this .joints                = new Set ();
   this .pointerAliases        = new Map ();
   this .animationScripts      = [ ];
   this .physicsNodes          = [ ];
}

Object .assign (Object .setPrototypeOf (GLTF2Parser .prototype, Parser_X3DParser .prototype),
   Parser_X3DOptimizer .prototype,
{
   getEncoding ()
   {
      return "JSON";
   },
   setInput (json)
   {
      try
      {
         if (typeof json === "string")
            json = JSON .parse (json);

         this .input = json;
      }
      catch
      {
         this .input = undefined;
      }
   },
   isValid: (() =>
   {
      const keys = new Set ([
         "accessors",
         "animations",
         "asset",
         "buffers",
         "bufferViews",
         "cameras",
         "extensions",
         "extensionsRequired",
         "extensionsUsed",
         "extras",
         "images",
         "materials",
         "meshes",
         "nodes",
         "samplers",
         "scene",
         "scenes",
         "skins",
         "textures",
      ]);

      return function ()
      {
         if (!(this .input instanceof Object))
            return false;

         if (!Object .keys (this .input) .every (key => keys .has (key)))
            return false;

         if (!(this .input .asset ?.version >= 2.0))
            return false;

         return true;
      };
   })(),
   setBuffers (buffers)
   {
      this .buffers = buffers;
   },
   parseIntoScene (resolve, reject)
   {
      this .rootObject (this .input)
         .then (resolve)
         .catch (reject);
   },
   async rootObject (glTF)
   {
      if (!(glTF instanceof Object))
         return;

      // Set profile and components.

      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("GLTF");
      scene .setProfile (browser .getProfile ("Interchange"));

      if (glTF .skins)
         scene .updateComponent (browser .getComponent ("HAnim"));

      // Parse root objects.

      this .assetObject (glTF .asset, glTF .extensions);
      await this .extensionsArray (glTF .extensionsRequired, this .extensions);
      await this .extensionsArray (glTF .extensionsUsed, this .extensions);
      this .extensionsObject (glTF .extensions);

      await browser .loadComponents (scene);
      await this .buffersArray (glTF .buffers);

      this .bufferViewsArray (glTF .bufferViews);
      this .accessorsArray   (glTF .accessors);
      this .samplersArray    (glTF .samplers);

      await this .imagesArray (glTF .images);
      await this .videosArray (glTF .extensions ?.EXT_texture_video ?.videos);

      this .texturesArray   (glTF .textures);
      this .materialsArray  (glTF .materials);
      this .meshesArray     (glTF .meshes);
      this .camerasArray    (glTF .cameras);
      this .skinsArray      (glTF .skins, glTF .nodes);
      this .nodesArray      (glTF .nodes);
      this .scenesArray     (glTF, glTF .scenes, glTF .scene);
      this .skinsHumanoid   ();
      this .skinsBBox       ();
      this .animationsArray (glTF .animations);

      this .viewpointsCenterOfRotation (scene);
      this .optimizeSceneGraph (scene .getRootNodes ());

      this .exportGroup ("EnvironmentLights", this .envLights);
      this .exportGroup ("Lights",            this .lights);
      this .exportGroup ("Viewpoints",        this .cameras);
      this .exportGroup ("Animations",        glTF .animations);

      this .cleanupAnimationScripts ();
      this .materialVariantsSwitch ();

      return scene;
   },
   assetObject (asset, extensions)
   {
      if (!(asset instanceof Object))
         return;

      const scene = this .getScene ();

      for (const [key, value] of Object .entries (asset))
      {
         if (typeof value !== "string")
            continue;

         if (key === "version")
            continue;

         scene .addMetaData (key, value);
      }

      if (asset .extras instanceof Object)
      {
         for (const [key, value] of Object .entries (asset .extras))
         {
            if (typeof value !== "string")
               continue;

            scene .addMetaData (key, value);
         }
      }

      if (asset .extensions ?.KHR_xmp_json_ld instanceof Object)
      {
         const packet = asset .extensions .KHR_xmp_json_ld .packet;

         this .khrXmpJsonLdObject (packet, extensions ?.KHR_xmp_json_ld);
      }

      if (extensions ?.VRM ?.meta instanceof Object)
      {
         for (const [key, value] of Object .entries (extensions .VRM .meta))
         {
            if (typeof value !== "string")
               continue;

            scene .addMetaData (key, value);
         }
      }
   },
   async extensionsArray (extensions, set)
   {
      if (!(extensions instanceof Array))
         return;

      const
         browser    = this .getBrowser (),
         scene      = this .getScene (),
         components = [ ];

      for (const extension of extensions)
      {
         set .add (extension);

         switch (extension)
         {
            case "EXT_lights_image_based":
            {
               components .push (browser .getComponent ("CubeMapTexturing", 3));
               break;
            }
            // https://github.com/KhronosGroup/glTF/pull/1956
            // case "KHR_lights_environment": // in development
            // {
            //    break;
            // },
            case "EXT_mesh_gpu_instancing":
            case "KHR_materials_anisotropy":
            case "KHR_materials_clearcoat":
            case "KHR_materials_diffuse_transmission":
            case "KHR_materials_dispersion":
            case "KHR_materials_emissive_strength":
            case "KHR_materials_ior":
            case "KHR_materials_iridescence":
            case "KHR_materials_pbrSpecularGlossiness":
            case "KHR_materials_sheen":
            case "KHR_materials_specular":
            case "KHR_materials_transmission":
            case "KHR_materials_volume_scatter":
            case "KHR_materials_volume":
            {
               components .push (browser .getComponent ("X_ITE", 1));
               break;
            }
            case "KHR_animation_pointer":
            {
               components .push (browser .getComponent ("EventUtilities", 1));
               components .push (browser .getComponent ("Scripting",      1));
               break;
            }
            case "KHR_draco_mesh_compression":
            {
               this .draco ??= await this .getLibrary ("draco_decoder_gltf.js");
               break;
            }
            case "KHR_gaussian_splatting":
            {
               components .push (browser .getComponent ("GaussianSplats", 1));
               break;
            }
            case "KHR_meshopt_compression":
            case "EXT_meshopt_compression":
            {
               this .MeshoptDecoder ??= await this .getLibrary ("meshopt_decoder.js");
               await this .MeshoptDecoder .ready;
               break;
            }
            case "KHR_texture_transform":
            {
               components .push (browser .getComponent ("Texturing3D", 2));
               break;
            }
         }
      }

      for (const component of components)
         scene .updateComponent (component);
   },
   getLibrary (file)
   {
      return this .constructor [file] ??= (async () =>
      {
         const
            response = await fetch (Networking_URLs .getLibraryURL (file)),
            text     = await response .text (),
            library  = await new Function (text) ();

         return library;
      })();
   },
   extensionsObject (extensions)
   {
      if (!(extensions instanceof Object))
         return;

      for (const [key, value] of Object .entries (extensions))
      {
         switch (key)
         {
            case "EXT_lights_image_based":
               this .extLightsImageBasedObject (value);
               break;
            // https://github.com/KhronosGroup/glTF/pull/1956
            // case "KHR_lights_environment": // in development
            //    this .khrLightsEnvironment (value);
            //    break;
            case "KHR_lights_punctual":
               this .khrLightsPunctualObject (value);
               break;
            case "KHR_materials_variants":
               this .khrMaterialsVariantsObjectVariants (value);
               break;
         }
      }
   },
   extLightsImageBasedObject (EXT_lights_image_based)
   {
      if (!(EXT_lights_image_based instanceof Object))
         return;

      this .envLightsArray (EXT_lights_image_based .lights);
   },
   envLightsArray (lights)
   {
      if (!(lights instanceof Array))
         return;

      this .envLights = lights;
   },
   envLightObject (id, light = this .envLights [id])
   {
      if (!(light instanceof Object))
         return null;

      if (light .node)
         return light .node;

      const
         scene      = this .getScene (),
         lightNode  = scene .createNode ("EnvironmentLight", false),
         name       = `EnvironmentLight${id + 1}`,
         quaternion = new Numbers_Quaternion ();

      scene .addNamedNode    (scene .getUniqueName       (name), lightNode);
      scene .addExportedNode (scene .getUniqueExportName (name), lightNode);

      lightNode ._global    = false;
      lightNode ._intensity = this .numberValue (light .intensity, 1);

      if (this .vectorValue (lightNode .rotation, quaternion))
         lightNode ._rotation = new Numbers_Rotation4 (0, 0, 1, Math .PI) .multRight (Numbers_Rotation4 .fromQuaternion (quaternion));
      else
         lightNode ._rotation = new Numbers_Rotation4 (0, 0, 1, Math .PI);

      if (light .irradianceCoefficients instanceof Array)
      {
         for (const irradianceCoefficient of light .irradianceCoefficients)
         {
            if (!(irradianceCoefficient instanceof Array))
               continue;

            lightNode ._diffuseCoefficients .push (... irradianceCoefficient);
         }
      }

      if (light .specularImages instanceof Array)
      {
         const
            specularTextureNode = scene .createNode ("ComposedCubeMapTexture", false),
            baseImages          = light .specularImages [0];

         if (baseImages instanceof Array)
         {
            const faces = ["right", "left", "bottom", "top", "front", "back"];

            for (const [i, image] of baseImages .map (image => this .images [image]) .entries ())
            {
               const
                  textureNode = scene .createNode ("ImageTexture", false),
                  name        = this .sanitizeName (image ?.name);

               if (name)
                  scene .addNamedNode (scene .getUniqueName (name), textureNode);

               textureNode ._description = image ?.name ?? `${GLTF2Parser_$.toUpperCaseFirst (faces [i])} Texture`;
               textureNode ._url         = image ? [image .uri] : [ ];
               textureNode .setup ();

               specularTextureNode [`_${faces [i]}Texture`] = textureNode;
            }

            specularTextureNode .setup ();

            lightNode ._specularTexture = specularTextureNode;
         }
      }

      lightNode .setup ();

      light .pointers = [lightNode];

      return light .node = lightNode;
   },
   khrLightsPunctualObject (KHR_lights_punctual)
   {
      if (!(KHR_lights_punctual instanceof Object))
         return;

      this .lightsArray (KHR_lights_punctual .lights);
   },
   lightsArray (lights)
   {
      if (!(lights instanceof Array))
         return;

      this .lights = lights;
   },
   lightObject (id, light = this .lights [id])
   {
      if (!(light instanceof Object))
         return null;

      if (light .node)
         return light .node;

      const lightNode = this .lightType (light);

      if (!lightNode)
         return null;

      const
         scene = this .getScene (),
         name  = this .sanitizeName (light .name) || `Light${id + 1}`;

      const color = new Numbers_Color3 (1);

      if (this .vectorValue (light .color, color))
         lightNode ._color = color;

      lightNode ._global    = true;
      lightNode ._intensity = this .numberValue (light .intensity, 1);

      lightNode .setup ();

      scene .addNamedNode    (scene .getUniqueName       (name), lightNode);
      scene .addExportedNode (scene .getUniqueExportName (name), lightNode);

      light .pointers = [lightNode];

      return light .node = lightNode;
   },
   lightType (light)
   {
      switch (light .type)
      {
         case "directional":
            return this .directionalLight (light);
         case "spot":
            return this .spotLight (light);
         case "point":
            return this .pointLight (light);
      }
   },
   directionalLight (light)
   {
      const
         scene     = this .getScene (),
         lightNode = scene .createNode ("DirectionalLight", false);

      return lightNode;
   },
   spotLight (light)
   {
      const
         scene     = this .getScene (),
         lightNode = scene .createNode ("SpotLight", false);

      lightNode ._radius      = this .numberValue (light .range, 0) || -1;
      lightNode ._cutOffAngle = this .numberValue (light .outerConeAngle, Math .PI / 4);
      lightNode ._beamWidth   = this .numberValue (light .innerConeAngle, 0);

      this .addAnimationPointerAlias (lightNode, "range",          "radius");
      this .addAnimationPointerAlias (lightNode, "outerConeAngle", "cutOffAngle");
      this .addAnimationPointerAlias (lightNode, "innerConeAngle", "beamWidth");

      return lightNode;
   },
   pointLight (light)
   {
      const
         scene     = this .getScene (),
         lightNode = scene .createNode ("PointLight", false);

      lightNode ._radius = this .numberValue (light .range, 0) || -1;

      this .addAnimationPointerAlias (lightNode, "range", "radius");

      return lightNode;
   },
   khrMaterialsVariantsObjectVariants (KHR_materials_variants)
   {
      if (!(KHR_materials_variants instanceof Object))
         return;

      const variants = KHR_materials_variants .variants;

      if (!(variants instanceof Array))
         return;

      this .materialVariants = variants;
   },
   khrXmpJsonLdObject (index, KHR_xmp_json_ld)
   {
      if (!(KHR_xmp_json_ld instanceof Object))
         return;

      const
         scene  = this .getScene (),
         packet = KHR_xmp_json_ld .packets [index];

      for (const [key, value] of Object .entries (packet))
      {
         const match = key .match (/\w+:(.*)/);

         if (!match)
            continue;

         const k = match [1];

         if (value instanceof Object)
         {
            const array = value ["@set"] ?? value ["@list"];

            if (array instanceof Array)
            {
               const v = array .map (v => v .toString ()) .join (", ");

               scene .addMetaData (k, v);
               continue;
            }

            if (value ["rdf:_1"] ?.["@value"])
            {
               const v = value ["rdf:_1"] ["@value"];

               scene .addMetaData (k, v);
               continue;
            }
         };

         if (typeof value !== "string")
            continue;

         const v = value;

         scene .addMetaData (k, v);
      }
   },
   async buffersArray (buffers)
   {
      if (!(buffers instanceof Array))
         return;

      this .buffers = await Promise .all (buffers .map ((buffer, i) => this .bufferObject (buffer, i)));
   },
   async bufferObject (buffer, i)
   {
      if (!(buffer instanceof Object))
         return;

      if (!buffer .uri)
         return this .buffers [i];

      this .getScene () .addLoadingObject (buffer);

      const
         url         = new x_ite_Fields .MFString (new URL (buffer .uri, this .getScene () .getBaseURL ())),
         arrayBuffer = await InputOutput_FileLoader .loadDocument (this .getBrowser () .getWorld (), url, { dataAsString: false });

      this .getScene () .removeLoadingObject (buffer);

      return arrayBuffer;
   },
   bufferViewsArray (bufferViews)
   {
      if (!(bufferViews instanceof Array))
         return;

      this .bufferViews = bufferViews;

      for (const bufferView of bufferViews)
         bufferView .getBuffer = (componentType) => this .bufferViewObject (bufferView, componentType);
   },
   bufferViewObject: (() =>
   {
      const ComponentSize = new Map ([
         [5120, 1], // Int8Array
         [5121, 1], // Uint8Array
         [5122, 2], // Int16Array
         [5123, 2], // Uint16Array
         [5124, 4], // Int32Array
         [5125, 4], // Uint32Array
         [5131, 2], // Float16Array
         [5126, 4], // Float32Array
         [5130, 8], // Float64Array
      ]);

      return function (bufferView, componentType)
      {
         if (!(bufferView instanceof Object))
            return;

         if (bufferView .buffer instanceof Object)
            return bufferView .buffer;

         const meshopt = bufferView .extensions ?.KHR_meshopt_compression
            ?? bufferView .extensions ?.EXT_meshopt_compression;

         if (meshopt instanceof Object)
         {
            const
               MeshoptDecoder = this .MeshoptDecoder,
               byteOffset     = meshopt .byteOffset || 0,
               byteLength     = meshopt .byteLength,
               byteStride     = meshopt .byteStride,
               count          = meshopt .count,
               mode           = meshopt .mode,
               filter         = meshopt .filter || "NONE",
               buffer         = this .buffers [meshopt .buffer];

            const
               componentSize   = ComponentSize .get (componentType),
               componentCount  = byteStride / componentSize,
               viewArrayLength = count * componentCount * componentSize,
               encoded         = new Uint8Array (buffer, byteOffset, byteLength),
               decoded         = new Uint8Array (viewArrayLength);

            MeshoptDecoder .decodeGltfBuffer (
               decoded,
               count,
               byteStride,
               encoded,
               mode,
               filter
            );

            return bufferView .buffer = decoded .buffer;
         }
         else
         {
            const buffer = this .buffers [bufferView .buffer];

            if (!buffer)
               return;

            const
               byteOffset = bufferView .byteOffset || 0,
               byteLength = bufferView .byteLength;

            return bufferView .buffer = buffer .slice (byteOffset, byteOffset + byteLength);
         }
      };
   })(),
   accessorsArray (accessors)
   {
      if (!(accessors instanceof Array))
         return;

      this .accessors = accessors;

      for (const accessor of accessors)
         this .accessorObject (accessor);
   },
   accessorObject: (() =>
   {
      // https://github.com/KhronosGroup/glTF/blob/2e51b3a2c5078a29f5c0d7aee567eb1d926265b4/extensions/2.0/Khronos/KHR_accessor_float64/README.md

      const TypedArrays = new Map ([
         [5120, Int8Array],
         [5121, Uint8Array],
         [5122, Int16Array],
         [5123, Uint16Array],
         [5124, Int32Array],
         [5125, Uint32Array],
         [5131, Float16Array],
         [5126, Float32Array],
         [5130, Float64Array],
      ]);

      const Components = new Map ([
         ["SCALAR", 1],
         ["VEC2",   2],
         ["VEC3",   3],
         ["VEC4",   4],
         ["MAT2",   4],
         ["MAT3",   9],
         ["MAT4",   16],
      ]);

      return function (accessor)
      {
         if (!(accessor instanceof Object))
            return;

         Object .defineProperty (accessor, "array",
         {
            get: () =>
            {
               const
                  TypedArray = TypedArrays .get (accessor .componentType),
                  bufferView = this .bufferViews [accessor .bufferView || 0],
                  byteOffset = accessor .byteOffset || 0,
                  byteStride = bufferView .byteStride || 0,
                  components = Components .get (accessor .type),
                  count      = accessor .count || 0,
                  stride     = byteStride ? byteStride / TypedArray .BYTES_PER_ELEMENT : components,
                  length     = Math .min (stride * count, (bufferView .byteLength - byteOffset) / TypedArray .BYTES_PER_ELEMENT),
                  array      = new TypedArray (bufferView .getBuffer (accessor .componentType), byteOffset, length);

               let value;

               value = this .denseArray (TypedArray, components, count, stride, array);
               value = this .sparseObject (accessor, components, value);
               value = this .normalizedArray (accessor, value);

               Object .defineProperty (accessor, "array", { value });

               return value;
            },
            configurable: true,
         });
      };
   })(),
   denseArray (TypedArray, components, count, stride, array)
   {
      if (stride === components)
         return array;

      const
         length = count * components,
         dense  = new TypedArray (length);

      for (let i = 0, j = 0; i < length; j += stride)
      {
         for (let c = 0; c < components; ++ c, ++ i)
            dense [i] = array [j + c];
      }

      return dense;
   },
   sparseObject: (() =>
   {
      const TypedArrays = new Map ([
         [5121, Uint8Array],
         [5123, Uint16Array],
         [5125, Uint32Array],
      ]);

      return function ({ sparse }, components, array)
      {
         if (!(sparse instanceof Object))
            return array;

         if (!(sparse .indices instanceof Object))
            return array;

         if (!(sparse .values instanceof Object))
            return array;

         const
            componentType     = sparse .indices .componentType,
            IndicesTypedArray = TypedArrays .get (componentType),
            indicesBufferView = this .bufferViews [sparse .indices .bufferView],
            indicesByteOffset = sparse .indices .byteOffset,
            indices           = new IndicesTypedArray (indicesBufferView .getBuffer (componentType), indicesByteOffset, sparse .count);

         const
            ValuesTypedArray = array .constructor,
            valuesBufferView = this .bufferViews [sparse .values .bufferView],
            valuesByteOffset = sparse .values .byteOffset,
            values           = new ValuesTypedArray (valuesBufferView .getBuffer (), valuesByteOffset, sparse .count * components);

         array = array .slice ();

         let v = 0;

         for (const i of indices)
         {
            for (let c = 0; c < components; ++ c, ++ v)
               array [i * components + c] = values [v];
         }

         return array;
      };
   })(),
   normalizedArray ({ normalized, componentType }, array)
   {
      if (!normalized)
         return array;

      switch (componentType)
      {
         case 5120: // Int8Array
            return Float32Array .from (array, v => Math .max (v / 127, -1));
         case 5121: // Uint8Array
            return Float32Array .from (array, v => v / 255);
         case 5122: // Int16Array
            return Float32Array .from (array, v => Math .max (v / 32767, -1));
         case 5123: // Uint16Array
            return Float32Array .from (array, v => v / 65535);
         case 5124: // Int32Array
            return Float32Array .from (array, v => Math .max (v / 2147483647, -1));
         case 5125: // Uint32Array
            return Float32Array .from (array, v => v / 4294967295);
         case 5131: // Float16Array
         case 5126: // Float32Array
         case 5130: // Float64Array
            return array; // Their normalized property MUST NOT be set to true;
      }
   },
   samplersArray (samplers)
   {
      if (!(samplers instanceof Array))
         return;

      this .samplers = samplers;

      for (const sampler of samplers)
         this .samplerObject (sampler);
   },
   samplerObject: (() =>
   {
      const MinificationFilters = new Map ([
         [9728, ["NEAREST_PIXEL",                false]],
         [9729, ["AVG_PIXEL",                    false]],
         [9984, ["NEAREST_PIXEL_NEAREST_MIPMAP", true]],
         [9985, ["AVG_PIXEL_NEAREST_MIPMAP",     true]],
         [9986, ["NEAREST_PIXEL_AVG_MIPMAP",     true]],
         [9987, ["AVG_PIXEL_AVG_MIPMAP",         true]],
      ]);

      const MagnificationFilters = new Map ([
         [9728, "NEAREST_PIXEL"],
         [9729, "AVG_PIXEL"],
      ]);

      const BoundaryModes = new Map ([
         [33071, "CLAMP_TO_EDGE"],
         [33648, "MIRRORED_REPEAT"],
         [10497, "REPEAT"],
      ]);

      return function (sampler)
      {
         if (!(sampler instanceof Object))
            return;

         Object .defineProperty (sampler, "texturePropertiesNode",
         {
            get: () =>
            {
               const
                  scene                 = this .getScene (),
                  texturePropertiesNode = scene .createNode ("TextureProperties", false),
                  name                  = this .sanitizeName (sampler .name);

               if (name)
                  scene .addNamedNode (scene .getUniqueName (name), texturePropertiesNode);

               // minFilter

               const minificationFilter = MinificationFilters .get (sampler .minFilter) ?? ["AVG_PIXEL_AVG_MIPMAP", true];

               texturePropertiesNode ._minificationFilter = minificationFilter [0];
               texturePropertiesNode ._generateMipMaps    = minificationFilter [1];

               // magFilter

               texturePropertiesNode ._magnificationFilter = MagnificationFilters .get (sampler .magFilter) ?? "AVG_PIXEL";

               // boundaryMode

               texturePropertiesNode ._boundaryModeS = BoundaryModes .get (sampler .wrapS) ?? "REPEAT";
               texturePropertiesNode ._boundaryModeT = BoundaryModes .get (sampler .wrapT) ?? "REPEAT";

               // anisotropicDegree

               texturePropertiesNode ._anisotropicDegree = this .getBrowser () .getRenderingProperty ("MaxAnisotropicDegree");

               // setup

               texturePropertiesNode .setup ();

               Object .defineProperty (sampler, "texturePropertiesNode", { value: texturePropertiesNode });

               return texturePropertiesNode;
            },
            configurable: true,
         });
      };
   })(),
   async imagesArray (images)
   {
      if (!(images instanceof Array))
         return;

      this .images = await Promise .all (images .map ((image, index) => this .imageObject (image, index)));
   },
   async videosArray (videos)
   {
      if (!(videos instanceof Array))
         return;

      this .videos = await Promise .all (videos .map ((video, index) => this .imageObject (video, index)));
   },
   async imageObject (image, index)
   {
      if (!(image instanceof Object))
         return;

      image .index = index;

      if (image .uri)
         return image;

      const bufferView = this .bufferViews [image .bufferView];

      if (!bufferView)
         return image;

      const
         buffer = bufferView .getBuffer (),
         blob   = new Blob ([new Uint8Array (buffer)], { type: image .mimeType }),
         uri    = await this .blobToDataUrl (blob);

      image .uri = uri;

      return image;
   },
   blobToDataUrl (blob)
   {
      return new Promise ((resolve, reject) =>
      {
         const fileReader = new FileReader ();

         fileReader .onload  = resolve;
         fileReader .onerror = reject;

         fileReader .readAsDataURL (blob);
      })
      .then (event => event .target .result);
   },
   texturesArray (textures)
   {
      if (!(textures instanceof Array))
         return;

      this .textures = textures;
   },
   textureObject (texture)
   {
      if (!(texture instanceof Object))
         return;

      if (texture .textureNode)
         return texture .textureNode;

      const
         videos = this .textureVideoObject (texture),
         images = this .textureImageObject (texture),
         medias = videos .length ? videos : images;

      if (!medias .length)
         return null;

      const
         key    = `${medias .map (media => media .index) .join (",")}:${texture .sampler}`,
         cached = this .textureCache .get (key);

      if (cached)
         return texture .textureNode = cached;

      const
         scene       = this .getScene (),
         textureNode = scene .createNode (videos .length ? "MovieTexture" : "ImageTexture", false),
         name        = this .sanitizeName (texture .name || medias [0] .name);

      if (name)
         scene .addNamedNode (scene .getUniqueName (name), textureNode);

      textureNode ._url                  = medias .map (image => image .uri);
      textureNode ._colorSpaceConversion = false;

      if (videos .length)
      {
         if (name)
            scene .addExportedNode (scene .getUniqueExportName (name), textureNode);

         textureNode ._loop = true;
      }

      const sampler = this .samplers [texture .sampler];

      if (sampler instanceof Object)
         textureNode ._textureProperties = sampler .texturePropertiesNode;

      textureNode .setup ();

      this .textureCache .set (key, textureNode);

      return texture .textureNode = textureNode;
   },
   textureVideoObject (texture)
   {
      const videos = [ ];

      if (this .extensions .has ("EXT_texture_video"))
         videos .push (this .videos [texture .extensions ?.EXT_texture_video ?.source]);

      return videos .filter (video => video);
   },
   textureImageObject (texture)
   {
      const images = [ ];

      this .textureImageExtensionsObject (texture .extensions, images)

      images .push (this .images [texture .source]);

      return images .filter (image => image);
   },
   textureImageExtensionsObject (extensions, images)
   {
      if (!(extensions instanceof Object))
         return;

      for (const [key, extension] of Object .entries (extensions))
      {
         if (!this .extensions .has (key))
            continue;

         switch (key)
         {
            case "KHR_texture_basisu":
            // case "EXT_texture_astc":
            case "EXT_texture_avif":
            case "EXT_texture_webp":
               images .push (this .images [extension ?.source]);
               break;
         }
      }
   },
   materialsArray (materials)
   {
      if (!(materials instanceof Array))
         return;

      this .materials = materials;
   },
   materialObject ({ material })
   {
      if (!(material instanceof Object))
         return this .getDefaultAppearance ();

      if (material .appearanceNode)
         return material .appearanceNode;

      const texCoordIndices = this .getTexCoordIndices ("", material);

      this .texCoordIndex           = Array .from (texCoordIndices) .reduce ((p, c) => Math .max (p, c), -1);
      this .textureTransformNodes   = [ ];
      this .texCoordMappings        = new Map ();
      this .texCoordOfNode          = new Map ();
      this .texCoordExtensionOfNode = new Map ();
      material .texCoordMappings    = this .texCoordMappings;

      const
         scene          = this .getScene (),
         appearanceNode = scene .createNode ("Appearance", false),
         materialNode   = this .createMaterial (material),
         name           = this .sanitizeName (material .name);

      const emissiveFactor = new Numbers_Color3 ();

      if (this .vectorValue (material .emissiveFactor, emissiveFactor))
         materialNode ._emissiveColor = emissiveFactor;

      materialNode ._emissiveTexture        = this .textureInfo    (material .emissiveTexture);
      materialNode ._emissiveTextureMapping = this .textureMapping (material .emissiveTexture);

      this .occlusionTextureInfo (material .occlusionTexture, materialNode);
      this .normalTextureInfo    (material .normalTexture,    materialNode);
      this .materialExtensions   (material .extensions,       materialNode);

      materialNode .setup ();

      for (const i of texCoordIndices)
      {
         const mapping = `TEXCOORD_${i}`;

         if (this .textureTransformNodes .length)
         {
            // If there are texture transform nodes, create a TextureTransform node for missing mappings.

            if (this .textureTransformNodes .every (node => node ._mapping !== mapping))
            {
               const textureTransformNode = scene .createNode ("TextureTransform", false);

               // Flip Y
               textureTransformNode ._mapping        = mapping;
               textureTransformNode ._translation .y = -1;
               textureTransformNode ._scale .y       = -1;

               textureTransformNode .setup ();

               this .textureTransformNodes .push (textureTransformNode);
            }
         }

         this .texCoordMappings .set (mapping, i);
      }

      if (name)
         scene .addNamedNode (scene .getUniqueName (name), appearanceNode);

      appearanceNode ._alphaMode        = this .stringValue (material .alphaMode, "OPAQUE");
      appearanceNode ._alphaCutoff      = this .numberValue (material .alphaCutoff, 0.5);
      appearanceNode ._material         = this .khrMaterialsUnlitObject (material .extensions ?.KHR_materials_unlit, materialNode);
      appearanceNode ._textureTransform = this .createMultiTextureTransform (appearanceNode ._material .getValue ());

      appearanceNode .setup ();

      material .pointers = [appearanceNode, materialNode];

      return material .appearanceNode = appearanceNode;
   },
   getTexCoordIndices (key, object, indices = new Set ())
   {
      if (!(object instanceof Object))
         return indices;

      if (key .endsWith ("Texture") && !object ?.extensions ?.KHR_texture_transform)
         indices .add (object .texCoord || 0);

      for (const [key, value] of Object .entries (object))
         this .getTexCoordIndices (key, value, indices);

      return indices;
   },
   createMaterial (material)
   {
      const materials = [
         this .pbrMetallicRoughnessObject  .bind (this, material .pbrMetallicRoughness),
         this .pbrSpecularGlossinessObject .bind (this, material .extensions ?.KHR_materials_pbrSpecularGlossiness),
         this .pbrMetallicRoughnessObject  .bind (this, { }),
      ];

      for (const material of materials)
      {
         const materialNode = material ();

         if (materialNode)
            return materialNode;
      }
   },
   pbrMetallicRoughnessObject (pbrMetallicRoughness)
   {
      if (!(pbrMetallicRoughness instanceof Object))
         return null;

      const
         scene        = this .getScene (),
         materialNode = scene .createNode ("PhysicalMaterial", false);

      const
         baseColorFactor = new Numbers_Color4 (),
         baseColor       = new Numbers_Color3 ();

      if (this .vectorValue (pbrMetallicRoughness .baseColorFactor, baseColorFactor))
      {
         materialNode ._baseColor    = baseColor .set (... baseColorFactor);
         materialNode ._transparency = 1 - baseColorFactor .a;
      }

      materialNode ._metallic  = this .numberValue (pbrMetallicRoughness .metallicFactor,  1);
      materialNode ._roughness = this .numberValue (pbrMetallicRoughness .roughnessFactor, 1);

      materialNode ._baseTexture                     = this .textureInfo (pbrMetallicRoughness .baseColorTexture);
      materialNode ._baseTextureMapping              = this .textureMapping (pbrMetallicRoughness .baseColorTexture);
      materialNode ._metallicRoughnessTexture        = this .textureInfo (pbrMetallicRoughness .metallicRoughnessTexture);
      materialNode ._metallicRoughnessTextureMapping = this .textureMapping (pbrMetallicRoughness .metallicRoughnessTexture);

      pbrMetallicRoughness .pointers = [materialNode];

      return materialNode;
   },
   pbrSpecularGlossinessObject (pbrSpecularGlossiness)
   {
      if (!(pbrSpecularGlossiness instanceof Object))
         return null;

      const
         scene        = this .getScene (),
         materialNode = scene .createNode ("SpecularGlossinessMaterial", false);

      const
         diffuseFactor  = new Numbers_Color4 (),
         diffuseColor   = new Numbers_Color3 (),
         specularFactor = new Numbers_Color3 ();

      if (this .vectorValue (pbrSpecularGlossiness .diffuseFactor, diffuseFactor))
      {
         materialNode ._diffuseColor = diffuseColor .set (... diffuseFactor);
         materialNode ._transparency = 1 - diffuseFactor .a;
      }

      materialNode ._diffuseTexture        = this .textureInfo (pbrSpecularGlossiness .diffuseTexture);
      materialNode ._diffuseTextureMapping = this .textureMapping (pbrSpecularGlossiness .diffuseTexture);

      if (this .vectorValue (pbrSpecularGlossiness .specularFactor, specularFactor))
         materialNode ._specularColor = specularFactor;
      else
         materialNode ._specularColor = Numbers_Color3 .WHITE;

      materialNode ._glossiness = this .numberValue (pbrSpecularGlossiness .glossinessFactor, 1);

      materialNode ._specularGlossinessTexture        = this .textureInfo (pbrSpecularGlossiness .specularGlossinessTexture);
      materialNode ._specularGlossinessTextureMapping = this .textureMapping (pbrSpecularGlossiness .specularGlossinessTexture);

      pbrSpecularGlossiness .pointers = [materialNode];

      return materialNode;
   },
   occlusionTextureInfo (occlusionTexture, materialNode)
   {
      if (!(occlusionTexture instanceof Object))
         return null;

      materialNode ._occlusionStrength       = this .numberValue (occlusionTexture .strength, 1);
      materialNode ._occlusionTexture        = this .textureInfo (occlusionTexture);
      materialNode ._occlusionTextureMapping = this .textureMapping (occlusionTexture);
   },
   normalTextureInfo (normalTexture, materialNode)
   {
      if (!(normalTexture instanceof Object))
         return null;

      materialNode ._normalScale          = this .numberValue (normalTexture .scale, 1);
      materialNode ._normalTexture        = this .textureInfo (normalTexture);
      materialNode ._normalTextureMapping = this .textureMapping (normalTexture);
   },
   textureInfo (texture)
   {
      if (!(texture instanceof Object))
         return null;

      if (texture .extensions instanceof Object)
         texture .mapping = this .textureTransformObject (texture .extensions .KHR_texture_transform, texture .texCoord || 0);
      else
         texture .mapping = `TEXCOORD_${texture .texCoord || 0}`;

      return this .textureObject (this .textures [texture .index]);
   },
   textureMapping (texture)
   {
      if (!(texture instanceof Object))
         return "";

      return texture .mapping;
   },
   materialExtensions (extensions, materialNode)
   {
      if (!(extensions instanceof Object))
         return;

      if (!materialNode .getType () .includes (Base_X3DConstants .PhysicalMaterial))
         return;

      for (const [key, value] of Object .entries (extensions))
      {
         switch (key)
         {
            case "KHR_materials_anisotropy":
               this .khrMaterialsAnisotropyObject (value, materialNode);
               break;
            case "KHR_materials_clearcoat":
               this .khrMaterialsClearcoatObject (value, materialNode);
               break;
            case "KHR_materials_diffuse_transmission":
               this .khrMaterialsDiffuseTransmissionObject (value, materialNode);
               break;
            case "KHR_materials_dispersion":
               this .khrMaterialsDispersionObject (value, materialNode);
               break;
            case "KHR_materials_emissive_strength":
               this .khrMaterialsEmissiveStrengthObject (value, materialNode);
               break;
            case "KHR_materials_ior":
               this .khrMaterialsIorStrengthObject (value, materialNode);
               break;
            case "KHR_materials_iridescence":
               this .khrMaterialsIridescenceObject (value, materialNode);
               break;
            case "KHR_materials_sheen":
               this .khrMaterialsSheenObject (value, materialNode);
               break;
            case "KHR_materials_specular":
               this .khrMaterialsSpecularObject (value, materialNode);
               break;
            case "KHR_materials_transmission":
               this .khrMaterialsTransmission (value, materialNode);
               break;
            case "KHR_materials_volume":
               this .khrMaterialsVolumeObject (value, materialNode);
               break;
            case "KHR_materials_volume_scatter":
               this .khrMaterialsVolumeScatterObject (value, materialNode);
               break;
         }
      }
   },
   khrMaterialsAnisotropyObject (KHR_materials_anisotropy, materialNode)
   {
      if (!(KHR_materials_anisotropy instanceof Object))
         return;

      const extension = this .getScene () .createNode ("AnisotropyMaterialExtension", false);

      extension ._anisotropyStrength       = this .numberValue (KHR_materials_anisotropy .anisotropyStrength, 0);
      extension ._anisotropyRotation       = this .numberValue (KHR_materials_anisotropy .anisotropyRotation, 0);
      extension ._anisotropyTexture        = this .textureInfo (KHR_materials_anisotropy .anisotropyTexture);
      extension ._anisotropyTextureMapping = this .textureMapping (KHR_materials_anisotropy .anisotropyTexture);

      extension .setup ();

      KHR_materials_anisotropy .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsClearcoatObject (KHR_materials_clearcoat, materialNode)
   {
      if (!(KHR_materials_clearcoat instanceof Object))
         return;

      const extension = this .getScene () .createNode ("ClearcoatMaterialExtension", false);

      extension ._clearcoat               = this .numberValue (KHR_materials_clearcoat .clearcoatFactor, 0);
      extension ._clearcoatTexture        = this .textureInfo (KHR_materials_clearcoat .clearcoatTexture);
      extension ._clearcoatTextureMapping = this .textureMapping (KHR_materials_clearcoat .clearcoatTexture);

      extension ._clearcoatRoughness               = this .numberValue (KHR_materials_clearcoat .clearcoatRoughnessFactor, 0);
      extension ._clearcoatRoughnessTexture        = this .textureInfo (KHR_materials_clearcoat .clearcoatRoughnessTexture);
      extension ._clearcoatRoughnessTextureMapping = this .textureMapping (KHR_materials_clearcoat .clearcoatRoughnessTexture);

      extension ._clearcoatNormalTexture        = this .textureInfo (KHR_materials_clearcoat .clearcoatNormalTexture);
      extension ._clearcoatNormalTextureMapping = this .textureMapping (KHR_materials_clearcoat .clearcoatNormalTexture);

      extension .setup ();

      KHR_materials_clearcoat .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsDiffuseTransmissionObject (KHR_materials_diffuse_transmission, materialNode)
   {
      if (!(KHR_materials_diffuse_transmission instanceof Object))
         return;

      const extension = this .getScene () .createNode ("DiffuseTransmissionMaterialExtension", false);

      extension ._diffuseTransmission               = this .numberValue (KHR_materials_diffuse_transmission .diffuseTransmissionFactor, 0);
      extension ._diffuseTransmissionTexture        = this .textureInfo (KHR_materials_diffuse_transmission .diffuseTransmissionTexture);
      extension ._diffuseTransmissionTextureMapping = this .textureMapping (KHR_materials_diffuse_transmission .diffuseTransmissionTexture);

      const diffuseTransmissionColorFactor = new Numbers_Color3 ();

      if (this .vectorValue (KHR_materials_diffuse_transmission .diffuseTransmissionColorFactor, diffuseTransmissionColorFactor))
         extension ._diffuseTransmissionColor = diffuseTransmissionColorFactor;

      extension ._diffuseTransmissionColorTexture        = this .textureInfo (KHR_materials_diffuse_transmission .diffuseTransmissionColorTexture);
      extension ._diffuseTransmissionColorTextureMapping = this .textureMapping (KHR_materials_diffuse_transmission .diffuseTransmissionColorTexture);

      extension .setup ();

      KHR_materials_diffuse_transmission .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsDispersionObject (KHR_materials_dispersion, materialNode)
   {
      if (!(KHR_materials_dispersion instanceof Object))
         return;

      const extension = this .getScene () .createNode ("DispersionMaterialExtension", false);

      extension ._dispersion = this .numberValue (KHR_materials_dispersion .dispersion, 0);

      extension .setup ();

      KHR_materials_dispersion .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsEmissiveStrengthObject (KHR_materials_emissive_strength, materialNode)
   {
      if (!(KHR_materials_emissive_strength instanceof Object))
         return;

      const extension = this .getScene () .createNode ("EmissiveStrengthMaterialExtension", false);

      extension ._emissiveStrength = this .numberValue (KHR_materials_emissive_strength .emissiveStrength, 1);

      extension .setup ();

      KHR_materials_emissive_strength .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsIorStrengthObject (KHR_materials_ior, materialNode)
   {
      const extension = this .getScene () .createNode ("IORMaterialExtension", false);

      extension ._indexOfRefraction = this .numberValue (KHR_materials_ior .ior, 1.5);

      extension .setup ();

      KHR_materials_ior .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsIridescenceObject (KHR_materials_iridescence, materialNode)
   {
      const extension = this .getScene () .createNode ("IridescenceMaterialExtension", false);

      extension ._iridescence                        = this .numberValue (KHR_materials_iridescence .iridescenceFactor, 0);
      extension ._iridescenceTexture                 = this .textureInfo (KHR_materials_iridescence .iridescenceTexture);
      extension ._iridescenceTextureMapping          = this .textureMapping (KHR_materials_iridescence .iridescenceTexture);
      extension ._iridescenceIndexOfRefraction       = this .numberValue (KHR_materials_iridescence .iridescenceIor, 1.3);
      extension ._iridescenceThicknessMinimum        = this .numberValue (KHR_materials_iridescence .iridescenceThicknessMinimum, 100);
      extension ._iridescenceThicknessMaximum        = this .numberValue (KHR_materials_iridescence .iridescenceThicknessMaximum, 400);
      extension ._iridescenceThicknessTexture        = this .textureInfo (KHR_materials_iridescence .iridescenceThicknessTexture);
      extension ._iridescenceThicknessTextureMapping = this .textureMapping (KHR_materials_iridescence .iridescenceThicknessTexture);

      extension .setup ();

      KHR_materials_iridescence .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsSheenObject (KHR_materials_sheen, materialNode)
   {
      if (!(KHR_materials_sheen instanceof Object))
         return;

      const extension = this .getScene () .createNode ("SheenMaterialExtension", false);

      const sheenColorFactor = new Numbers_Color3 ();

      if (this .vectorValue (KHR_materials_sheen .sheenColorFactor, sheenColorFactor))
         extension ._sheenColor = sheenColorFactor;

      extension ._sheenColorTexture        = this .textureInfo (KHR_materials_sheen .sheenColorTexture);
      extension ._sheenColorTextureMapping = this .textureMapping (KHR_materials_sheen .sheenColorTexture);

      extension ._sheenRoughness               = this .numberValue (KHR_materials_sheen .sheenRoughnessFactor, 0);
      extension ._sheenRoughnessTexture        = this .textureInfo (KHR_materials_sheen .sheenRoughnessTexture);
      extension ._sheenRoughnessTextureMapping = this .textureMapping (KHR_materials_sheen .sheenRoughnessTexture);

      extension .setup ();

      KHR_materials_sheen .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsSpecularObject (KHR_materials_specular, materialNode)
   {
      if (!(KHR_materials_specular instanceof Object))
         return;

      const extension = this .getScene () .createNode ("SpecularMaterialExtension", false);

      extension ._specular               = this .numberValue (KHR_materials_specular .specularFactor, 1);
      extension ._specularTexture        = this .textureInfo (KHR_materials_specular .specularTexture);
      extension ._specularTextureMapping = this .textureMapping (KHR_materials_specular .specularTexture);

      const specularColorFactor = new Numbers_Vector3 ();

      if (this .vectorValue (KHR_materials_specular .specularColorFactor, specularColorFactor))
      {
         const
            specularStrength = Math .max (... specularColorFactor),
            specularColor    = [... specularColorFactor] .map (c => c / specularStrength);

         extension ._specularColor    = new Numbers_Color3 (... (specularStrength ? specularColor : [0]));
         extension ._specularStrength = specularStrength;
      }

      extension ._specularColorTexture        = this .textureInfo (KHR_materials_specular .specularColorTexture);
      extension ._specularColorTextureMapping = this .textureMapping (KHR_materials_specular .specularColorTexture);

      extension .setup ();

      KHR_materials_specular .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsTransmission (KHR_materials_transmission, materialNode)
   {
      if (!(KHR_materials_transmission instanceof Object))
         return;

      const extension = this .getScene () .createNode ("TransmissionMaterialExtension", false);

      extension ._transmission               = this .numberValue (KHR_materials_transmission .transmissionFactor, 0);
      extension ._transmissionTexture        = this .textureInfo (KHR_materials_transmission .transmissionTexture);
      extension ._transmissionTextureMapping = this .textureMapping (KHR_materials_transmission .transmissionTexture);

      extension .setup ();

      KHR_materials_transmission .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsVolumeObject (KHR_materials_volume, materialNode)
   {
      const extension = this .getScene () .createNode ("VolumeMaterialExtension", false);

      extension ._thickness               = this .numberValue (KHR_materials_volume .thicknessFactor, 0);
      extension ._thicknessTexture        = this .textureInfo (KHR_materials_volume .thicknessTexture);
      extension ._thicknessTextureMapping = this .textureMapping (KHR_materials_volume .thicknessTexture);
      extension ._attenuationDistance     = this .numberValue (KHR_materials_volume .attenuationDistance, 1_000_000);

      const attenuationColor = new Numbers_Color3 ();

      if (this .vectorValue (KHR_materials_volume .attenuationColor, attenuationColor))
         extension ._attenuationColor = attenuationColor;

      extension .setup ();

      KHR_materials_volume .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsVolumeScatterObject (KHR_materials_volume_scatter, materialNode)
   {
      if (!(KHR_materials_volume_scatter instanceof Object))
         return;

      const extension = this .getScene () .createNode ("VolumeScatterMaterialExtension", false);

      extension ._scatterStrength        = this .numberValue (KHR_materials_volume_scatter .scatterStrengthFactor, 0);
      extension ._scatterStrengthTexture = this .textureInfo (KHR_materials_volume_scatter .scatterStrengthTexture);
      extension ._scatterTextureMapping  = this .textureMapping (KHR_materials_volume_scatter .scatterTexture);

      const multiscatterColor = new Numbers_Color3 ();

      if (this .vectorValue (KHR_materials_volume_scatter .multiscatterColorFactor, multiscatterColor))
         extension ._multiscatterColor = multiscatterColor;

      extension ._multiscatterColorTexture        = this .textureInfo (KHR_materials_volume_scatter .multiscatterColorTexture);
      extension ._multiscatterColorTextureMapping = this .textureMapping (KHR_materials_volume_scatter .multiscatterColorTexture);
      extension ._scatterAnisotropy               = this .numberValue (KHR_materials_volume_scatter .scatterAnisotropy, 0);

      extension .setup ();

      KHR_materials_volume_scatter .pointers = [extension];

      materialNode ._extensions .push (extension);
   },
   khrMaterialsUnlitObject (KHR_materials_unlit, materialNode)
   {
      if (!KHR_materials_unlit)
         return materialNode;

      const unlitMaterialNode = this .getScene () .createNode ("UnlitMaterial", false);

      unlitMaterialNode ._emissiveColor          = materialNode ._baseColor;
      unlitMaterialNode ._emissiveTextureMapping = materialNode ._baseTextureMapping;
      unlitMaterialNode ._emissiveTexture        = materialNode ._baseTexture;
      unlitMaterialNode ._normalScale            = materialNode ._normalScale;
      unlitMaterialNode ._normalTextureMapping   = materialNode ._normalTextureMapping;
      unlitMaterialNode ._normalTexture          = materialNode ._normalTexture;
      unlitMaterialNode ._transparency           = materialNode ._transparency;

      unlitMaterialNode .setup ();
      this .addAnimationPointerAlias (unlitMaterialNode, "baseColor", "emissiveColor");

      materialNode .dispose ();

      return unlitMaterialNode;
   },
   textureTransformObject: (() =>
   {
      const
         matrix   = new Numbers_Matrix4 (),
         vector   = new Numbers_Vector3 (),
         rotation = new Numbers_Rotation4 ();

      return function (KHR_texture_transform, texCoord)
      {
         if (!(KHR_texture_transform instanceof Object))
            return;

         if (!this .extensions .has ("KHR_texture_transform"))
            return;

         texCoord = KHR_texture_transform .texCoord ?? texCoord;

         const
            translation = new Numbers_Vector2 (),
            scale       = new Numbers_Vector2 (1);

         // Reset matrix.
         matrix .set ();

         // Flip Y
         matrix .scale (vector .set (1, -1, 1));
         matrix .translate (vector .set (0, -1, 0));

         if (this .vectorValue (KHR_texture_transform .offset, translation))
            matrix .translate (vector .set (... translation, 0));

         const angle = this .numberValue (KHR_texture_transform .rotation, 0)

         matrix .rotate (rotation .set (0, 0, -1, angle));

         if (this .vectorValue (KHR_texture_transform .scale, scale))
            matrix .scale (vector .set (... scale, 1));

         // Check for existing node.

         const existing = this .textureTransformNodes .find (node => this .texCoordOfNode .get (node) === texCoord && node ._matrix .getValue () .equals (matrix));

         if (existing)
         {
            Object .defineProperty (KHR_texture_transform, "pointers",
            {
               get: () =>
               {
                  return this .texCoordExtensionOfNode .get (existing);
               },
               configurable: true,
            });

            return existing ._mapping .getValue ();
         }

         // Create new TextureTransformMatrix3D.

         const
            scene                = this .getScene (),
            textureTransformNode = scene .createNode ("TextureTransformMatrix3D", false),
            mapping              = `TEXCOORD_${this .texCoordIndex + this .textureTransformNodes .length + 1}`;

         textureTransformNode ._mapping = mapping;
         textureTransformNode ._matrix  = matrix;

         textureTransformNode .setup ();

         this .textureTransformNodes .push (textureTransformNode);
         this .texCoordMappings .set (mapping, texCoord);
         this .texCoordOfNode .set (textureTransformNode, texCoord);
         this .texCoordExtensionOfNode .set (textureTransformNode, KHR_texture_transform);

         Object .defineProperty (KHR_texture_transform, "pointers",
         {
            get: () =>
            {
               const scriptNode = scene .createNode ("Script", false);

               scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "translation",   new x_ite_Fields .SFVec2f (... translation));
               scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "rotation",      new x_ite_Fields .SFFloat (angle));
               scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "scale",         new x_ite_Fields .SFVec2f (... scale));
               scriptNode .addUserDefinedField (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFMatrix4f ());

               scriptNode ._url = [/* js */ `ecmascript:

const m = new SFMatrix3f ();

function eventsProcessed ()
{
   m .setTransform (translation, -rotation, scale);

   // m' = m * flipY

   value_changed [0]  = m [0];
   value_changed [1]  = -m [1] + m [2];
   value_changed [4]  = m [3];
   value_changed [5]  = -m [4] + m [5];
   value_changed [12] = m [6];
   value_changed [13] = -m [7] + m [8];
}
`];

               scriptNode .setup ();

               scene .addNamedNode (scene .getUniqueName ("CombineTextureMatrixScript"), scriptNode);
               scene .addRoute (scriptNode, "value_changed", textureTransformNode, "set_matrix");

               this .addAnimationPointerAlias (scriptNode, "offset", "translation");
               this .animationScripts .push (scriptNode);

               Object .defineProperty (KHR_texture_transform, "pointers", { value: [scriptNode] });

               return [scriptNode];
            },
            configurable: true,
         });

         return mapping;
      };
   })(),
   meshesArray (meshes)
   {
      if (!(meshes instanceof Array))
         return;

      this .meshes = meshes;
   },
   meshObject (mesh, skin, EXT_mesh_gpu_instancing)
   {
      if (!(mesh instanceof Object))
         return;

      if (mesh .shapeNodes && !EXT_mesh_gpu_instancing)
      {
         const primitives = mesh .primitives;

         if (!(primitives instanceof Array))
            return mesh .shapeNodes;

         for (const primitive of primitives)
            this .attributesJointsArray (skin, primitive .attributes ?.JOINTS, primitive .attributes ?.WEIGHTS);

         return mesh .shapeNodes;
      }

      const shapeNodes = this .primitivesArray (mesh, skin, EXT_mesh_gpu_instancing);

      // Name Shape nodes.

      const
         scene = this .getScene (),
         name  = this .sanitizeName (mesh .name);

      if (name)
      {
         for (const shapeNode of shapeNodes)
            scene .addNamedNode (scene .getUniqueName (name), shapeNode);
      }

      return mesh .shapeNodes = shapeNodes;
   },
   primitivesArray ({ primitives, weights }, skin, EXT_mesh_gpu_instancing)
   {
      if (!(primitives instanceof Array))
         return [ ];

      const shapeNodes = [ ];

      for (const primitive of primitives)
         this .primitiveObject (primitive, weights, skin, EXT_mesh_gpu_instancing, shapeNodes);

      return shapeNodes;
   },
   primitiveObject (primitive, weights, skin, EXT_mesh_gpu_instancing, shapeNodes)
   {
      if (!(primitive instanceof Object))
         return;

      this .attributesObject (primitive .attributes);
      this .targetsArray     (primitive .targets);

      if (primitive .extensions ?.KHR_gaussian_splatting)
         return this .khrGaussianSplatting (primitive, shapeNodes);

      primitive .indices  = this .accessors [primitive .indices];
      primitive .material = this .materials [primitive .material];

      this .primitiveExtensionsObject (primitive .extensions, primitive);

      const
         shapeNode    = this .createShape (primitive, weights, skin, EXT_mesh_gpu_instancing),
         variantsNode = this .khrMaterialsVariantsExtension (primitive .extensions ?.KHR_materials_variants, shapeNode);

      shapeNodes .push (primitive .shapeNode = variantsNode ?? shapeNode);
   },
   khrGaussianSplatting (primitive, shapeNodes)
   {
      const
         scene                  = this .getScene (),
         gaussianSplats         = scene .createNode ("GaussianSplats", false),
         KHR_gaussian_splatting = primitive .extensions ?.KHR_gaussian_splatting,
         attributes             = primitive .attributes;

      gaussianSplats ._colorSpace = String (KHR_gaussian_splatting ?.colorSpace ?? "SRGB_REC709_DISPLAY") .toUpperCase ();

      gaussianSplats ._positions    = attributes ?.POSITION ?.array ?? [ ];
      gaussianSplats ._orientations = attributes ?.["KHR_gaussian_splatting:ROTATION"] ?.array ?? [ ];
      gaussianSplats ._scales       = attributes ?.["KHR_gaussian_splatting:SCALE"]    ?.array ?? [ ];
      gaussianSplats ._opacities    = attributes ?.["KHR_gaussian_splatting:OPACITY"]  ?.array ?? [ ];

      // Degrees 0,1,2,3

      for (const [degree, coefs] of [1, 3, 5, 7] .entries ())
      {
         for (let coef = 0; coef < coefs; ++ coef)
         {
            const
               field = gaussianSplats .getField (`sphericalHarmonicsDegree${degree}Coef${coef}`),
               value = attributes ?.[`KHR_gaussian_splatting:SH_DEGREE_${degree}_COEF_${coef}`] ?.array ?? [ ];

            field .setValue (value);
         }
      }

      // Bounding Box

      const
         min    = new Numbers_Vector3 (),
         max    = new Numbers_Vector3 (),
         hasMin = this .vectorValue (attributes ?.POSITION ?.min, min),
         hasMax = this .vectorValue (attributes ?.POSITION ?.max, max);

      if (hasMin && hasMax)
      {
         gaussianSplats ._bboxSize   = max .copy () .subtract (min),
         gaussianSplats ._bboxCenter = min .copy () .add (max) .divide (2);
      }

      gaussianSplats .setup ();

      shapeNodes .push (primitive .shapeNode = gaussianSplats);
   },
   attributesObject (attributes)
   {
      if (!(attributes instanceof Object))
         return;

      for (const key in attributes)
         attributes [key] = this .accessors [attributes [key]];

      attributes .TEXCOORD = [ ];
      attributes .COLOR    = [ ];
      attributes .JOINTS   = [ ];
      attributes .WEIGHTS  = [ ];

      for (let i = 0; attributes ["TEXCOORD_" + i]; ++ i)
         attributes .TEXCOORD .push (attributes ["TEXCOORD_" + i]);

      for (let i = 0; attributes ["COLOR_" + i]; ++ i)
         attributes .COLOR .push (attributes ["COLOR_" + i]);

      for (let i = 0; attributes ["JOINTS_" + i]; ++ i)
         attributes .JOINTS .push (attributes ["JOINTS_" + i]);

      for (let i = 0; attributes ["WEIGHTS_" + i]; ++ i)
         attributes .WEIGHTS .push (attributes ["WEIGHTS_" + i]);
   },
   targetsArray (targets)
   {
      if (!(targets instanceof Array))
         return;
   },
   primitiveExtensionsObject (extensions, primitive)
   {
      if (!(extensions instanceof Object))
         return;

      for (const [key, value] of Object .entries (extensions))
      {
         switch (key)
         {
            case "KHR_draco_mesh_compression":
               this .khrDracoMeshCompressionObject (value, primitive);
               break;
         }
      }
   },
   khrDracoMeshCompressionObject (draco, primitive)
   {
      if (!(draco instanceof Object))
         return;

      if (!this .draco)
         return;

      function indicesCallback (value)
      {
         Object .defineProperty (primitive .indices, "array", { value });
      }

      function attributeCallback (key, value)
      {
         if (attributes [key])
            Object .defineProperty (attributes [key], "array", { value });
      }

      const
         attributes = primitive .attributes,
         dataView   = new Uint8Array (this .bufferViews [draco .bufferView] .getBuffer ());

      this .dracoDecodeMesh (this .draco, dataView, draco .attributes, indicesCallback, attributeCallback);
   },
   dracoDecodeMesh (draco, dataView, attributes, indicesCallback, attributeCallback)
   {
      const
         buffer  = new draco .DecoderBuffer (),
         decoder = new draco .Decoder ();

      buffer .Init (dataView, dataView .byteLength);

      let geometry, status;

      try
      {
         const type = decoder .GetEncodedGeometryType (buffer);

         switch (type)
         {
            case draco .TRIANGULAR_MESH:
               geometry = new draco .Mesh ();
               status   = decoder .DecodeBufferToMesh (buffer, geometry);
               break;
            case draco .POINT_CLOUD:
               geometry = new draco .PointCloud ();
               status   = decoder .DecodeBufferToPointCloud (buffer, geometry);
               break;
            default:
               throw new Error (`Invalid geometry type ${type}.`);
         }

         if (!status .ok () || !geometry .ptr)
            throw new Error (status .error_msg ());

         if (type === draco .TRIANGULAR_MESH)
         {
            const
               numFaces   = geometry .num_faces (),
               numIndices = numFaces * 3,
               byteLength = numIndices * 4,
               ptr        = draco ._malloc (byteLength);

            try
            {
               const indices = new Uint32Array (numIndices);

               decoder .GetTrianglesUInt32Array (geometry, byteLength, ptr);

               indices .set (new Uint32Array (draco .HEAPF32 .buffer, ptr, numIndices));

               indicesCallback (indices);
            }
            finally
            {
               draco ._free (ptr);
            }
         }

         for (const [key, id] of Object .entries (attributes))
         {
            const
               attribute     = decoder .GetAttributeByUniqueId (geometry, id),
               numComponents = attribute .num_components (),
               numPoints     = geometry .num_points (),
               numValues     = numPoints * numComponents,
               byteLength    = numValues * Float32Array .BYTES_PER_ELEMENT,
               ptr           = draco ._malloc (byteLength);

            try
            {
               const array = new Float32Array (numValues);

               decoder .GetAttributeDataArrayForAllPoints (geometry, attribute, draco .DT_FLOAT32, byteLength, ptr);

               array .set (new Float32Array (draco .HEAPF32 .buffer, ptr, numValues));

               attributeCallback (key, array);
            }
            finally
            {
               draco ._free (ptr);
            }
         }
      }
      finally
      {
         if (geometry)
            draco .destroy (geometry);

         draco .destroy (decoder);
         draco .destroy (buffer);
      }
   },
   khrMaterialsVariantsExtension (KHR_materials_variants, shapeNode)
   {
      if (!(KHR_materials_variants instanceof Object))
         return;

      const mappings = KHR_materials_variants .mappings;

      if (!(mappings instanceof Array))
         return;

      if (!mappings .length)
         return;

      const
         scene        = this .getScene (),
         variantsNode = scene .createNode ("Switch", false);

      for (const mapping of mappings)
         this .khrMaterialsVariantsObjectMapping (mapping, shapeNode, variantsNode);

      variantsNode ._whichChoice = this .materialVariants .length;

      if (!variantsNode ._children .length)
         return;

      // Last child ist default material.
      variantsNode ._children [this .materialVariants .length] = shapeNode;

      // Fall back to default material if no active variant.
      for (const i of variantsNode ._children .keys ())
      {
         if (!variantsNode ._children [i])
            variantsNode ._children [i] = shapeNode;
      }

      variantsNode .setup ();

      this .materialVariantNodes .push (variantsNode);

      return variantsNode;
   },
   khrMaterialsVariantsObjectMapping (mapping, shapeNode, variantsNode)
   {
      if (!(mapping instanceof Object))
         return;

      mapping .material = this .materials [mapping .material];

      if (!mapping .material)
         return;

      const
         scene          = this .getScene (),
         variantNode    = this .getScene () .createNode ("Shape", false),
         appearanceNode = this .materialObject (mapping),
         variant        = mapping .variants ?.[0] ?? 0,
         name           = this .sanitizeName (this .materialVariants [variant] ?.name ?? "");

      if (name)
         scene .addNamedNode (scene .getUniqueName (name), variantNode);

      variantNode ._appearance = appearanceNode;
      variantNode ._geometry   = shapeNode ._geometry;

      variantNode .setup ();

      variantsNode ._children [variant] = variantNode;
   },
   camerasArray (cameras)
   {
      if (!(cameras instanceof Array))
         return;

      this .cameras = cameras;
   },
   cameraObject (id, camera)
   {
      if (!(camera instanceof Object))
         return null;

      if (camera .node !== undefined)
         return camera .node;

      const viewpointNode = this .cameraType (camera);

      if (!viewpointNode)
         return camera .node = null;

      const
         scene = this .getScene (),
         name  = this .sanitizeName (camera .name);

      // Name

      if (name)
      {
         scene .addNamedNode    (scene .getUniqueName       (name), viewpointNode);
         scene .addExportedNode (scene .getUniqueExportName (name), viewpointNode);
      }

      viewpointNode ._description = this .description (camera .name || `Viewpoint ${id + 1}`);
      viewpointNode ._position    = Numbers_Vector3 .ZERO;

      return camera .node = viewpointNode;
   },
   cameraType (camera)
   {
      switch (camera .type)
      {
         case "orthographic":
            return this .orthographicCamera (camera .orthographic);
         case "perspective":
            return this .perspectiveCamera (camera .perspective);
         default:
            return null;
      }
   },
   orthographicCamera (camera)
   {
      if (!(camera instanceof Object))
         return null;

      const
         scene         = this .getScene (),
         viewpointNode = scene .createNode ("OrthoViewpoint", false),
         xmag          = typeof camera .xmag === "number" ? camera .xmag : 1,
         ymag          = typeof camera .ymag === "number" ? camera .ymag : 1;

      viewpointNode ._fieldOfView [0] = -xmag;
      viewpointNode ._fieldOfView [1] = -ymag;
      viewpointNode ._fieldOfView [2] =  xmag;
      viewpointNode ._fieldOfView [3] =  ymag;

      if (typeof camera .znear === "number")
         viewpointNode ._nearDistance = camera .znear;

      if (typeof camera .zfar === "number")
         viewpointNode ._farDistance = camera .zfar;

      viewpointNode .setup ();

      this .addAnimationPointerAlias (viewpointNode, "znear", "nearDistance");
      this .addAnimationPointerAlias (viewpointNode, "zfar",  "farDistance");

      Object .defineProperty (camera, "pointers",
      {
         get: () =>
         {
            const scriptNode = scene .createNode ("Script", false);

            scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "xmag",          new x_ite_Fields .SFFloat (xmag));
            scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "ymag",          new x_ite_Fields .SFFloat (ymag));
            scriptNode .addUserDefinedField (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .MFFloat ());

            scriptNode ._url = [/* js */ `ecmascript:

function eventsProcessed ()
{
   value_changed [0] = -xmag;
   value_changed [1] = -ymag;
   value_changed [2] =  xmag;
   value_changed [3] =  ymag;
}
`];

            scriptNode .setup ();

            scene .addNamedNode (scene .getUniqueName ("CombineFieldOfViewScript"), scriptNode);
            scene .addRoute (scriptNode, "value_changed", viewpointNode, "set_fieldOfView");

            this .animationScripts .push (scriptNode);

            Object .defineProperty (camera, "pointers", { value: [viewpointNode, scriptNode] });

            return [viewpointNode, scriptNode];
         },
         configurable: true,
      });

      return viewpointNode;
   },
   perspectiveCamera (camera)
   {
      if (!(camera instanceof Object))
         return null;

      const
         scene         = this .getScene (),
         viewpointNode = scene .createNode ("Viewpoint", false);

      if (typeof camera .yfov === "number")
         viewpointNode ._fieldOfView = camera .yfov;

      if (typeof camera .znear === "number")
         viewpointNode ._nearDistance = camera .znear;

      if (typeof camera .zfar === "number")
         viewpointNode ._farDistance = camera .zfar;

      viewpointNode .setup ();

      this .addAnimationPointerAlias (viewpointNode, "yfov",  "fieldOfView");
      this .addAnimationPointerAlias (viewpointNode, "znear", "nearDistance");
      this .addAnimationPointerAlias (viewpointNode, "zfar",  "farDistance");

      camera .pointers = [viewpointNode];

      return viewpointNode;
   },
   nodesArray (nodes)
   {
      if (!(nodes instanceof Array))
         return;

      this .nodes = nodes .map ((node, index) => this .nodeObject (node, index));

      // 1. Replace skeleton nodes with humanoid.
      // 2. Add children.

      this .nodes .forEach ((node, index) => this .nodeSkeleton (node, index));
      this .nodes .forEach ((node, index) => this .nodeChildren (node, index));
   },
   nodeObject (node, index)
   {
      if (!(node instanceof Object))
         return { };

      if (node .transformNode)
         return node;

      // Create Transform or HAnimJoint.

      const
         scene         = this .getScene (),
         typeName      = this .joints .has (index) ? "HAnimJoint" : "Transform",
         transformNode = scene .createNode (typeName, false);

      node .transformNode = transformNode;
      node .childNode     = transformNode;
      node .pointers      = [transformNode];

      return node;
   },
   nodeSkeleton (node, index)
   {
      const skin = this .skins [node .skin];

      if (!skin)
         return;

      const
         skeletons    = skin .skeleton .map (index => this .nodes [index]) .filter (node => node),
         humanoidNode = skin .humanoidNode,
         childNode    = skin .childNode;

      node .transformNode = childNode;

      for (const skeleton of skeletons)
      {
         skeleton .humanoidNode = humanoidNode;
         skeleton .childNode    = childNode;

         // Only make the first skeleton node a HAnimHumanoid node.
         break;
      }
   },
   nodeChildren: (() =>
   {
      const
         translation      = new Numbers_Vector3 (),
         rotation         = new Numbers_Rotation4 (),
         scale            = new Numbers_Vector3 (),
         scaleOrientation = new Numbers_Rotation4 (),
         quaternion       = new Numbers_Quaternion (),
         matrix           = new Numbers_Matrix4 ();

      return function (node, index)
      {
         const
            scene         = this .getScene (),
            skin          = this .skins [node .skin],
            transformNode = node .transformNode,
            name          = this .sanitizeName (node .name);

         // Name

         if (name && !skin)
         {
            scene .addNamedNode (scene .getUniqueName (name), transformNode);

            if (transformNode .getType () .at (-1) === Base_X3DConstants .HAnimJoint)
               transformNode ._name = node .name;
         }

         // Get mesh.

         const
            EXT_mesh_gpu_instancing = node .extensions ?.EXT_mesh_gpu_instancing,
            shapeNodes              = this .meshObject (this .meshes [node .mesh], skin, EXT_mesh_gpu_instancing);

         // Add camera.

         const viewpointNode = this .cameraObject (node .camera, this .cameras [node .camera]);

         if (viewpointNode)
            transformNode ._children .push (viewpointNode);

         // Handle extensions.

         this .nodeExtensions (node);

         // Add children.

         let children = this .nodeChildrenArray (node .children);

         if (transformNode .getType () .at (-1) === Base_X3DConstants .HAnimJoint)
         {
            // Add a HAnimSegment if there are recursive skeletons or Transform nodes as children.

            children = children .map (childNode =>
            {
               if (childNode .getType () .at (-1) !== Base_X3DConstants .HAnimJoint)
               {
                  const segmentNode = scene .createNode ("HAnimSegment", false);

                  segmentNode ._children .push (childNode);

                  segmentNode .setup ();

                  return segmentNode;
               }
               else
               {
                  return childNode;
               }
            });
         }

         transformNode ._children .push (... children);

         if (skin)
         {
            skin .name ??= node .name;

            const humanoidNode = skin .humanoidNode;

            if (!shapeNodes ?.length)
               return;

            humanoidNode ._skinNormal = shapeNodes [0] ._geometry .normal;
            humanoidNode ._skinCoord  = shapeNodes [0] ._geometry .coord;

            humanoidNode ._skin .push (... shapeNodes);
         }
         else
         {
            // Set transformation matrix.

            if (this .vectorValue (node .matrix, matrix))
            {
               matrix .getTransform (translation, rotation, scale, scaleOrientation);

               transformNode ._translation      = translation;
               transformNode ._rotation         = rotation;
               transformNode ._scale            = scale;
               transformNode ._scaleOrientation = scaleOrientation;
            }
            else
            {
               if (this .vectorValue (node .translation, translation))
                  transformNode ._translation = translation;

               if (this .vectorValue (node .rotation, quaternion))
                  transformNode ._rotation = rotation .setQuaternion (quaternion);

               if (this .vectorValue (node .scale, scale))
                  transformNode ._scale = scale;
            }

            // Add Shape nodes.

            if (shapeNodes)
               transformNode ._children .push (... shapeNodes);

            transformNode .setup ();
         }
      };
   })(),
   nodeExtensions (node)
   {
      if (!(node .extensions instanceof Object))
         return;

      for (const [key, extension] of Object .entries (node .extensions))
      {
         if (!(extension instanceof Object))
            continue;

         switch (key)
         {
            case "KHR_lights_punctual":
            {
               const lightNode = this .lightObject (extension .light);

               if (!lightNode)
                  break;

               node .transformNode ._children .push (lightNode);
               break;
            }
            case "KHR_node_visibility":
            {
               // https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_node_visibility
               extension .pointers       = [node .childNode];
               node .childNode ._visible = extension .visible ?? true;
               break;
            }
         }
      }
   },
   nodeChildrenArray (children)
   {
      if (!(children instanceof Array))
         return [ ];

      const nodes = Array .from (new Set (children
         .map (index => this .nodes [index])
         .filter (node => node ?.skin === undefined)
         .map (node => node ?.childNode)
         .filter (node => node)
      ));

      return nodes;
   },
   skinsArray (skins, nodes)
   {
      if (!(skins instanceof Array))
         return;

      if (!(nodes instanceof Array))
         return;

      this .skins = skins;

      for (const [i, skin] of skins .entries ())
         this .skinObject (i, skin, nodes);
   },
   skinObject (index, skin, nodes)
   {
      if (!(skin instanceof Object))
         return;

      const
         scene    = this .getScene (),
         skeleton = skin .skeleton;

      skin .joints              = this .jointsArray (skin .joints, nodes .some (node => node .skin === index));
      skin .skeleton            = skeleton !== undefined ? [skeleton] : this .skeleton (skin .joints, nodes);
      skin .inverseBindMatrices = this .inverseBindMatricesAccessors (this .accessors [skin .inverseBindMatrices]);

      if (skeleton !== undefined && !skin .joints .includes (skeleton))
      {
         // Ensure skeleton root node becomes a HAnimJoint node.

         this .joints .add (skeleton);
         skin .joints .push (skeleton);
      }

      const commons = this .skeletons .getOrInsert (skin .skeleton [0], { });

      commons .childNode      ??= scene .createNode ("Transform",  false);
      commons .normalNode     ??= scene .createNode ("Normal",     false);
      commons .coordinateNode ??= scene .createNode ("Coordinate", false);

      skin .childNode                  = commons .childNode;
      skin .normalNode                 = commons .normalNode;
      skin .coordinateNode             = commons .coordinateNode;
      skin .humanoidNode               = scene .createNode ("HAnimHumanoid",          false);
      skin .textureCoordinateNode      = scene .createNode ("TextureCoordinate",      false);
      skin .multiTextureCoordinateNode = scene .createNode ("MultiTextureCoordinate", false);

      skin .childNode ._children .push (skin .humanoidNode);
      skin .textureCoordinateNode ._mapping = "TEXCOORD_0";

      if (skin .childNode ._children .length === 1)
      {
         skin .childNode      .setup ();
         skin .normalNode     .setup ();
         skin .coordinateNode .setup ();
      }

      skin .textureCoordinateNode      .setup ();
      skin .multiTextureCoordinateNode .setup ();
   },
   jointsArray (joints, add)
   {
      if (!(joints instanceof Array))
         return [ ];

      // If skin is not references anywhere, don't create HAnimJoint nodes.

      if (add)
         joints .forEach (index => this .joints .add (index));

      return joints;
   },
   skeleton (joints, nodes)
   {
      const children = new Set ();

      joints .forEach (index => this .skeletonChildren (index, children));

      return joints .filter (index => !children .has (index));
   },
   skeletonChildren (index, set)
   {
      // Must use nodes from input here.
      const node = this .input .nodes [index];

      if (!(node instanceof Object))
         return;

      const children = node .children;

      if (!(children instanceof Array))
         return;

      for (const child of children)
      {
         set .add (child);
         this .skeletonChildren (child, set);
      }
   },
   inverseBindMatricesAccessors (inverseBindMatrices)
   {
      if (!inverseBindMatrices)
         return [ ];

      const
         array    = inverseBindMatrices .array,
         length   = array .length,
         matrices = [ ];

      for (let i = 0; i < length; i += 16)
         matrices .push (new Numbers_Matrix4 (... array .subarray (i, i + 16)));

      return matrices;
   },
   skinsHumanoid ()
   {
      this .skins .forEach (skin => this .skinHumanoid (skin))
   },
   skinHumanoid: (() =>
   {
      const
         translation = new Numbers_Vector3 (),
         rotation    = new Numbers_Rotation4 (),
         scale       = new Numbers_Vector3 ();

      return function (skin)
      {
         const
            scene        = this .getScene (),
            humanoidNode = skin .humanoidNode,
            name         = this .sanitizeName (skin .name);

         if (name)
            scene .addNamedNode (scene .getUniqueName (name), humanoidNode);

         humanoidNode ._name = skin .name ?? "";

         if (this .input .extensions ?.VRM)
            humanoidNode ._skeletalConfiguration = "VRM";
         else
            humanoidNode ._skeletalConfiguration = "GLTF";

         humanoidNode ._skeleton .push (... skin .skeleton
            .map (index => this .nodes [index] ?.transformNode) .filter (node => node));

         for (const [i, joint] of skin .joints .entries ())
         {
            const
               jointNode         = this .nodes [joint] ?.transformNode,
               inverseBindMatrix = skin .inverseBindMatrices [i] ?? Numbers_Matrix4 .IDENTITY;

            if (!jointNode)
               continue;

            inverseBindMatrix .getTransform (translation, rotation, scale);

            humanoidNode ._joints                .push (jointNode);
            humanoidNode ._jointBindingPositions .push (translation);
            humanoidNode ._jointBindingRotations .push (rotation);
            humanoidNode ._jointBindingScales    .push (scale);
         }

         humanoidNode .setup ();
      };
   })(),
   skinsBBox ()
   {
      this .skins .forEach (skin => this .skinBBox (skin .humanoidNode))
   },
   skinBBox (humanoidNode)
   {
      if (!humanoidNode ._skinCoord .getValue ())
         return;

      const
         jointMatrix = new Numbers_Matrix4 (),
         layerNode   = new Layering_Layer (this .getScene ());

      // Generate joint model matrices.

      layerNode .setup ();
      layerNode .getViewport () .push (layerNode);

      humanoidNode .traverse (Rendering_TraverseType .DISPLAY, layerNode);

      // Determine points at binding position.

      const
         jointBindingPositions = humanoidNode ._jointBindingPositions,
         jointBindingRotations = humanoidNode ._jointBindingRotations,
         jointBindingScales    = humanoidNode ._jointBindingScales,
         skinCoord             = humanoidNode ._skinCoord,
         points                = Array .from (skinCoord .point, p => p .getValue () .copy ()),
         skinPoints            = [ ];

      for (const [j, jointNode] of humanoidNode ._joints .entries ())
      {
         const skinCoordWeight = jointNode .skinCoordWeight;

         jointMatrix .setTransform (jointBindingPositions [j] ?.getValue (),
                                    jointBindingRotations [j] ?.getValue (),
                                    jointBindingScales [j] ?.getValue ())
         .multRight (jointNode .getValue () .getModelViewMatrix ());

         for (const [c, index] of jointNode .skinCoordIndex .entries ())
         {
            const
               point = points [index],
               skin  = skinPoints [index] ??= point .copy ();

            skin ?.add (jointMatrix .multVecMatrix (point .copy ()) .subtract (point) .multiply (skinCoordWeight [c]));
         }
      }

      // Set bbox for all Shape nodes.

      const
         bbox       = Geometry_Box3 .fromPoints (Object .values (skinPoints)),
         bboxSize   = bbox .size,
         bboxCenter = bbox .center;

      for (const shapeNode of humanoidNode ._skin)
      {
         shapeNode .bboxSize   = bboxSize;
         shapeNode .bboxCenter = bboxCenter;
      }
   },
   scenesArray (glTF, scenes, sceneNumber = 0)
   {
      if (!(scenes instanceof Array))
         return;

      const
         scene    = this .getScene (),
         children = scenes .map ((scene, index) => this .sceneObject (scene, index)) .filter (node => node);

      switch (children .length)
      {
         case 0:
         {
            return;
         }
         case 1:
         {
            if (sceneNumber === 0)
            {
               scene .getRootNodes () .push (children [0]);
               return;
            }

            // falls through
         }
         default:
         {
            // Root

            const switchNode = scene .createNode ("Switch", false);

            scene .addNamedNode    (scene .getUniqueName       ("Scenes"), switchNode);
            scene .addExportedNode (scene .getUniqueExportName ("Scenes"), switchNode);

            // Scenes.

            switchNode ._whichChoice = sceneNumber;
            switchNode ._children    = children;

            switchNode .setup ();

            this .addAnimationPointerAlias (switchNode, "scene", "whichChoice");

            glTF .pointers = [switchNode];

            scene .getRootNodes () .push (switchNode);
            return;
         }
      }
   },
   sceneObject (scene, index)
   {
      if (!(scene instanceof Object))
         return null;

      const
         lightNode = this .envLightObject (scene .extensions ?.EXT_lights_image_based ?.light),
         nodes     = this .sceneNodesArray (scene .nodes);

      if (lightNode)
         nodes .unshift (lightNode);

      switch (nodes .length)
      {
         case 0:
         {
            return null;
         }
         case 1:
         {
            const
               scene = this .getScene (),
               node  = nodes [0];

            if (!node .getName ())
               scene .addNamedNode (scene .getUniqueName (`Scene${index}`), node);

            return node;
         }
         default:
         {
            const
               scene     = this .getScene (),
               groupNode = scene .createNode ("Group", false),
               name      = this .sanitizeName (scene .name) || `Scene${index}`;

            scene .addNamedNode (scene .getUniqueName (name), groupNode);

            groupNode ._children = nodes;

            groupNode .setup ();

            return groupNode;
         }
      }
   },
   sceneNodesArray (nodes)
   {
      return this .nodeChildrenArray (nodes);
   },
   exportGroup (name, array)
   {
      if (!(array instanceof Array))
         return;

      const nodes = array .map (object => object .node) .filter (node => node);

      if (!nodes .length)
         return;

      const
         scene     = this .getScene (),
         groupNode = scene .createNode ("Group", false);

      scene .addNamedNode    (scene .getUniqueName       (name), groupNode);
      scene .addExportedNode (scene .getUniqueExportName (name), groupNode);

      groupNode ._visible  = false;
      groupNode ._children = nodes;

      groupNode .setup ();

      scene .getRootNodes () .push (groupNode);
   },
   materialVariantsSwitch ()
   {
      if (!this .materialVariantNodes .length)
         return;

      const
         scene      = this .getScene (),
         switchNode = scene .createNode ("Switch", false),
         names      = this .materialVariants .map (object => object .name);

      scene .addNamedNode    (scene .getUniqueName       ("MaterialVariants"), switchNode);
      scene .addExportedNode (scene .getUniqueExportName ("MaterialVariants"), switchNode);

      switchNode ._whichChoice = 0;
      switchNode ._visible     = false;

      switchNode .setup ();
      switchNode .setMetaData ("MaterialVariants/names", new x_ite_Fields .MFString (... names));

      for (const variantNode of this .materialVariantNodes)
         scene .addRoute (switchNode, "whichChoice", variantNode, "whichChoice");

      scene .getRootNodes () .push (switchNode);
   },
   animationsArray (animations)
   {
      if (!(animations instanceof Array))
         return;

      for (const [i, animation] of animations .entries ())
         this .animationObject (i, animation);
   },
   animationObject (id, animation)
   {
      if (!(animation instanceof Object))
         return null;

      const
         scene          = this .getScene (),
         timeSensorNode = scene .createNode ("TimeSensor", false),
         channelNodes   = this .animationChannelsArray (animation .channels, animation .samplers, timeSensorNode);

      if (!channelNodes .length)
         return;

      const
         groupNode = scene .createNode ("Group", false),
         name      = this .sanitizeName (animation .name);

      scene .addNamedNode (scene .getUniqueName (name || `Animation${id + 1}`), groupNode);
      scene .addNamedNode (scene .getUniqueName (`Timer${id + 1}`), timeSensorNode);
      scene .addExportedNode (scene .getUniqueExportName (name || `Animation${id + 1}`), groupNode);
      scene .addExportedNode (scene .getUniqueExportName (`Timer${id + 1}`), timeSensorNode);

      timeSensorNode ._description = this .description (animation .name) || `Animation ${id + 1}`;

      groupNode ._children .push (timeSensorNode, ... channelNodes);

      timeSensorNode .setup ();
      groupNode .setup ();

      animation .node = groupNode;
   },
   animationChannelsArray (channels, samplers, timeSensorNode)
   {
      if (!(channels instanceof Array))
         return [ ];

      if (!(samplers instanceof Array))
         return [ ];

      // Determine cycleInterval.

      const cycleInterval = samplers
         .map (sampler => this .accessors [sampler .input])
         .filter (input => input ?.array .length)
         .reduce ((value, input) => Math .max (value, input .array .at (-1)), 0);

      timeSensorNode ._cycleInterval = cycleInterval;

      // Get interpolators.

      channels = channels
         .flatMap (channel => this .animationChannelObject (channel, samplers, timeSensorNode));

      // Insert Script nodes after last interpolator.

      for (const scriptNode of this .animationScripts)
      {
         const index = channels .findLastIndex (node => node .getFields ()
            .some (field => Array .from (field .getOutputRoutes ())
            .some (route => route .getDestinationNode () === scriptNode)));

         channels .splice (index + 1, 0, scriptNode);
      }

      this .animationScripts .length = 0;

      return channels;
   },
   animationChannelObject (channel, samplers, timeSensorNode)
   {
      if (!(channel instanceof Object))
         return [ ];

      const target = channel .target;

      if (!(target instanceof Object))
         return [ ];

      const node = this .nodes [target .node] ?.transformNode;

      if (!node && target .path !== "pointer")
         return [ ];

      const sampler = samplers [channel .sampler];

      if (!sampler)
         return [ ];

      const input = this .accessors [sampler .input];

      if (!input)
         return [ ];

      if (!input .array .length)
         return [ ];

      const output = this .accessors [sampler .output];

      if (!output)
         return [ ];

      if (!output .array .length)
         return [ ];

      return this .createInterpolator (timeSensorNode, node, target, sampler .interpolation, input .array, output, timeSensorNode ._cycleInterval .getValue ());
   },
   cleanupAnimationScripts ()
   {
      // It can happen, that some scripts are not used, so let us remove them.

      const
         scene       = this .getScene (),
         scriptNodes = Array .from (scene .getNamedNodes (), node => node .getValue ())
            .filter (node => node .getType () .at (-1) === Base_X3DConstants .Script);

      for (const scriptNode of scriptNodes)
      {
         if (scriptNode .getFields () .every (field => !field .getInputRoutes () .size))
         {
            scriptNode .dispose ();
            continue;
         }

         if (scriptNode .getFields () .every (field => !field .getOutputRoutes () .size))
         {
            scriptNode .dispose ();
            continue;
         }
      }
   },
   createShape (primitive, weights, skin, EXT_mesh_gpu_instancing)
   {
      const
         scene     = this .getScene (),
         shapeNode = this .meshInstancing (EXT_mesh_gpu_instancing) ?? scene .createNode ("Shape", false);

      primitive .appearanceNode ??= this .materialObject (primitive);
      primitive .geometryNode   ??= this .createGeometry (primitive, weights, skin);

      shapeNode ._appearance = primitive .appearanceNode;
      shapeNode ._geometry   = primitive .geometryNode;

      shapeNode .setup ();

      return shapeNode;
   },
   meshInstancing (EXT_mesh_gpu_instancing)
   {
      if (!(EXT_mesh_gpu_instancing instanceof Object))
         return null;

      let
         attributes  = EXT_mesh_gpu_instancing .attributes,
         translation = this .accessors [attributes ?.TRANSLATION],
         rotation    = this .accessors [attributes ?.ROTATION],
         scale       = this .accessors [attributes ?.SCALE],
         count       = Math .max (translation ?.count ?? 0, rotation ?.count ?? 0, scale ?.count ?? 0);

      if (!count)
         return null;

      if (translation ?.type !== "VEC3")
         translation = null;

      if (rotation ?.type !== "VEC4")
         rotation = null;

      if (scale ?.type !== "VEC3")
         scale = null;

      const
         scene              = this .getScene (),
         instancedShapeNode = scene .createNode ("InstancedShape", false),
         translationArray   = translation ?.array,
         rotationArray      = rotation ?.array,
         scaleArray         = scale ?.array;

      if (translationArray)
         instancedShapeNode ._translations = translationArray;

      if (rotationArray)
      {
         const length = rotation .count * 4;

         for (let i = 0; i < length; i += 4)
         {
            instancedShapeNode ._rotations .push (Numbers_Rotation4 .fromQuaternion (new Numbers_Quaternion (rotationArray [i + 0],
                                                                                             rotationArray [i + 1],
                                                                                             rotationArray [i + 2],
                                                                                             rotationArray [i + 3])));
         }
      }

      if (scaleArray)
         instancedShapeNode ._scales = scaleArray;

      instancedShapeNode .setup ();

      return instancedShapeNode;
   },
   getDefaultAppearance ()
   {
      if (this .defaultAppearance)
         return this .defaultAppearance;

      const
         scene          = this .getScene (),
         appearanceNode = scene .createNode ("Appearance", false),
         materialNode   = scene .createNode ("PhysicalMaterial", false);

      appearanceNode ._alphaMode = "OPAQUE";
      appearanceNode ._material  = materialNode;
      materialNode   ._metallic  = 0;

      materialNode   .setup ();
      appearanceNode .setup ();

      return this .defaultAppearance = appearanceNode;
   },
   hasTextures (materialNode)
   {
      // Test PhysicalMaterial, UnlitMaterial ...

      if (+materialNode .getTextureBits ())
         return true;

      if (materialNode ._extensions ?.some (extension => +extension .getValue () .getTextureBits ()))
         return true;

      return false;
   },
   createMultiTextureTransform (materialNode)
   {
      if (!this .hasTextures (materialNode))
         return null;

      const textureTransformNodes = this .textureTransformNodes
         .sort ((a, b) => Math_Algorithm .cmp (a ._mapping .getValue (), b ._mapping .getValue ()));

      switch (textureTransformNodes .length)
      {
         case 0:
         {
            return this .textureTransformNode ??= (() =>
            {
               const
                  scene                = this .getScene (),
                  textureTransformNode = scene .createNode ("TextureTransform", false);

               // Flip Y
               textureTransformNode ._translation .y = -1;
               textureTransformNode ._scale .y       = -1;

               textureTransformNode .setup ();

               return textureTransformNode;
            })();
         }
         case 1:
         {
            return textureTransformNodes [0];
         }
         default:
         {
            const
               scene                = this .getScene (),
               textureTransformNode = scene .createNode ("MultiTextureTransform", false);

            textureTransformNode ._textureTransform = textureTransformNodes;

            textureTransformNode .setup ();

            return textureTransformNode;
         }
      }
   },
   createGeometry (primitive, weights, skin)
   {
      switch (primitive .mode)
      {
         case 0: // POINTS
         {
            return this .createPointSet (primitive, weights, skin);
         }
         case 1: // LINES
         {
            if (primitive .indices)
               return this .createIndexedLineSet (primitive, weights, skin, 1);

            return this .createLineSet (primitive, weights, skin);
         }
         case 2: // LINE_LOOP
         {
            return this .createIndexedLineSet (primitive, weights, skin, 2);
         }
         case 3: // LINE_STRIP
         {
            return this .createIndexedLineSet (primitive, weights, skin, 3);
         }
         default:
         case 4: // TRIANGLES
         {
            if (primitive .indices)
               return this .createIndexedTriangleSet (primitive, weights, skin);

            return this .createTriangleSet (primitive, weights, skin);
         }
         case 5: // TRIANGLE_STRIP
         {
            if (primitive .indices)
               return this .createIndexedTriangleStripSet (primitive, weights, skin);

            return this .createTriangleStripSet (primitive, weights, skin);
         }
         case 6: // TRIANGLE_FAN
         {
            if (primitive .indices)
               return this .createIndexedTriangleFanSet (primitive, weights, skin);

            return this .createTriangleFanSet (primitive, weights, skin);
         }
      }
   },
   createPointSet ({ attributes, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("PointSet", false);

      geometryNode ._color   = this .createColor (attributes .COLOR [0], material);
      geometryNode ._normal  = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord   = this .createCoordinate (attributes .POSITION, targets, weights);

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createIndexedLineSet ({ attributes, indices, material, targets }, weights, skin, mode)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("IndexedLineSet", false);

      geometryNode ._color   = this .createColor (attributes .COLOR [0], material);
      geometryNode ._normal  = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord   = this .createCoordinate (attributes .POSITION, targets, weights);

      switch (mode)
      {
         case 1: // LINES
         {
            const
               coordIndex = geometryNode ._coordIndex,
               array      = indices .array,
               length     = array .length;

            for (let i = 0; i < length; i += 2)
               coordIndex .push (array [i], array [i + 1], -1);

            break;
         }
         case 2: // LINE_LOOP
         {
            const coordIndex = geometryNode ._coordIndex;

            if (indices)
            {
               for (const i of indices .array)
                  coordIndex .push (i);

               if (coordIndex .length)
                  coordIndex .push (coordIndex [0], -1);
            }
            else
            {
               const coord = geometryNode ._coord;

               if (coord ?.point .length)
               {
                  for (const i of coord .point .keys ())
                     coordIndex .push (i);

                  coordIndex .push (0, -1);
               }
            }

            break;
         }
         case 3: // LINE_STRIP
         {
            const coordIndex = geometryNode ._coordIndex;

            if (indices)
            {
               for (const i of indices .array)
                  coordIndex .push (i);

               if (coordIndex .length)
                  coordIndex .push (-1);
            }
            else
            {
               const coord = geometryNode ._coord;

               if (coord ?.point .length)
               {
                  for (const i of coord .point .keys ())
                     coordIndex .push (i);

                  coordIndex .push (-1);
               }
            }

            break;
         }
      }

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createLineSet ({ attributes, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("LineSet", false);

      geometryNode ._color   = this .createColor (attributes .COLOR [0], material);
      geometryNode ._normal  = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord   = this .createCoordinate (attributes .POSITION, targets, weights);

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createIndexedTriangleSet ({ attributes, indices, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("IndexedTriangleSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._index           = indices .array;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createTriangleSet ({ attributes, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("TriangleSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createIndexedTriangleStripSet ({ attributes, indices, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("IndexedTriangleStripSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._index           = indices .array;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createTriangleStripSet ({ attributes, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("TriangleStripSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      const coord = geometryNode ._coord;

      if (coord)
      {
         if (coord .point .length)
            geometryNode ._stripCount = [coord .point .length];
      }

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createIndexedTriangleFanSet ({ attributes, indices, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("IndexedTriangleFanSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._index           = indices .array;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createTriangleFanSet ({ attributes, material, targets }, weights, skin)
   {
      const
         scene        = this .getScene (),
         geometryNode = scene .createNode ("TriangleFanSet", false);

      geometryNode ._solid           = !material ?.doubleSided;
      geometryNode ._color           = this .createColor (attributes .COLOR [0], material);
      geometryNode ._texCoord        = this .createMultiTextureCoordinate (attributes .TEXCOORD, material);
      geometryNode ._normal          = this .createNormal (attributes .NORMAL, targets, weights);
      geometryNode ._tangent         = this .createTangent (attributes .TANGENT, attributes .NORMAL);
      geometryNode ._coord           = this .createCoordinate (attributes .POSITION, targets, weights);
      geometryNode ._normalPerVertex = !! geometryNode ._normal .getValue ();

      const coord = geometryNode ._coord;

      if (coord)
      {
         if (coord .point .length)
            geometryNode ._fanCount = [coord .point .length];
      }

      this .attributesJointsArray (skin, attributes .JOINTS, attributes .WEIGHTS);
      this .skinGeometry (skin, geometryNode);

      geometryNode .setup ();

      return geometryNode;
   },
   createColor: (() =>
   {
      const TypeNames = new Map ([
         ["VEC3", "Color"],
         ["VEC4", "ColorRGBA"],
      ]);

      return function (color, material)
      {
         if (!(color instanceof Object))
            return null;

         const typeName = TypeNames .get (color .type);

         if (!typeName)
            return null;

         if (color .colorNode)
            return color .colorNode;

         const
            scene          = this .getScene (),
            appearanceNode = this .materialObject ({ material }),
            opaque         = appearanceNode ._alphaMode .getValue () === "OPAQUE",
            colorNode      = scene .createNode (opaque ? "Color" : typeName, false);

         colorNode ._color = opaque && typeName !== "Color"
            ? color .array .filter ((_, i) => (i + 1) % 4)
            : color .array;

         colorNode .setup ();

         return color .colorNode = colorNode;
      };
   })(),
   createMultiTextureCoordinate (texCoords, material)
   {
      const
         appearanceNode = this .materialObject ({ material }),
         materialNode   = appearanceNode ._material .getValue ();

      if (!this .hasTextures (materialNode))
         return null;

      if (texCoords .textureCoordinateNode)
         return texCoords .textureCoordinateNode;

      switch (texCoords .length === 1 ? 1 : material .texCoordMappings .size)
      {
         case 0:
         {
            return null;
         }
         case 1:
         {
            return texCoords .textureCoordinateNode = Array .from (material .texCoordMappings .entries (), ([mapping, i]) => this .createTextureCoordinate (texCoords [i], mapping)) [0];
         }
         default:
         {
            const textureCoordinateNodes = Array .from (material .texCoordMappings .entries ())
               .sort ((a, b) => Math_Algorithm .cmp (a [0], b [0]))
               .sort ((a, b) => a [1] - b [1])
               .map (([mapping, i]) => this .createTextureCoordinate (texCoords [i], mapping));

            if (!textureCoordinateNodes .length)
               return null;

            const
               scene                 = this .getScene (),
               textureCoordinateNode = scene .createNode ("MultiTextureCoordinate", false);

            textureCoordinateNode ._texCoord = textureCoordinateNodes;

            textureCoordinateNode .setup ();

            return texCoords .textureCoordinateNode = textureCoordinateNode;
         }
      }
   },
   createTextureCoordinate (texCoord, mapping)
   {
      if (texCoord ?.type !== "VEC2")
         return null;

      if (texCoord [mapping])
         return texCoord [mapping];

      const
         scene                 = this .getScene (),
         textureCoordinateNode = scene .createNode ("TextureCoordinate", false);

      textureCoordinateNode ._mapping = mapping;
      textureCoordinateNode ._point   = texCoord .array;

      textureCoordinateNode .setup ();

      return texCoord [mapping] = textureCoordinateNode;
   },
   createNormal (normal, targets, weights)
   {
      if (normal ?.type !== "VEC3")
         return null;

      if (normal .normalNode)
         return normal .normalNode;

      const
         scene      = this .getScene (),
         normalNode = scene .createNode ("Normal", false);

      normalNode ._vector = normal .array;

      if ((targets instanceof Array) && (weights instanceof Array))
      {
         normal .field = normalNode ._vector .copy ();

         const vectors = this .applyMorphTargets (normalNode ._vector, targets, "NORMAL", weights);

         normalNode ._vector .length = 0;

         for (const vector of vectors)
            normalNode ._vector .push (vector);
      }
      else
      {
         normal .field = normalNode ._vector;
      }

      normalNode .setup ();

      return normal .normalNode = normalNode;
   },
   createTangent (tangent, normal)
   {
      // When the base mesh primitive does not specify normals, client implementations
      // MUST calculate flat normals for each morph target; the provided tangents and
      // their displacements (if present) MUST be ignored.

      if (normal ?.type !== "VEC3")
         return null;

      if (tangent ?.type !== "VEC4")
         return null;

      if (tangent .tangentNode)
         return tangent .tangentNode;

      const
         scene       = this .getScene (),
         tangentNode = scene .createNode ("Tangent", false);

      tangentNode ._vector = tangent .array;

      tangentNode .setup ();

      return tangent .tangentNode = tangentNode;
   },
   createCoordinate (position, targets, weights)
   {
      if (position ?.type !== "VEC3")
         return null;

      if (position .coordinateNode)
         return position .coordinateNode;

      const
         scene          = this .getScene (),
         coordinateNode = scene .createNode ("Coordinate", false);

      coordinateNode ._point = position .array;

      if ((targets instanceof Array) && (weights instanceof Array))
      {
         position .field = coordinateNode ._point .copy ();

         const points = this .applyMorphTargets (coordinateNode ._point, targets, "POSITION", weights);

         coordinateNode ._point .length = 0;

         for (const point of points)
            coordinateNode ._point .push (point);
      }
      else
      {
         position .field = coordinateNode ._point;
      }

      coordinateNode .setup ();

      return position .coordinateNode = coordinateNode;
   },
   attributesJointsArray (skin, joints, weights)
   {
      if (!(skin instanceof Object))
         return;

      if (!(joints instanceof Array))
         return;

      if (!(weights instanceof Array))
         return;

      for (let i = 0, length = joints .length; i < length; ++ i)
         this .attributesJointsObject (skin, joints [i], weights [i]);
   },
   attributesJointsObject (skin, joints, weights)
   {
      if (joints ?.type !== "VEC4")
         return;

      if (weights ?.type !== "VEC4")
         return;

      const
         start        = skin .coordinateNode ._point .length,
         jointsArray  = joints .array,
         weightsArray = weights .array,
         numVertices  = jointsArray .length / 4;

      for (let v = 0; v < numVertices; ++ v)
      {
         for (let i = 0; i < 4; ++ i)
         {
            const w = weightsArray [v * 4 + i];

            if (w === 0)
               continue;

            const
               index     = skin .joints [jointsArray [v * 4 + i]],
               jointNode = this .nodes [index] ?.transformNode;

            if (jointNode ?.getType () .at (-1) !== Base_X3DConstants .HAnimJoint)
               continue;

            jointNode ._skinCoordIndex  .push (v + start);
            jointNode ._skinCoordWeight .push (w);
         }
      }
   },
   skinGeometry (skin, geometryNode)
   {
      if (!(skin instanceof Object))
         return;

      const
         skinCoordinateNode    = skin .coordinateNode,
         start                 = skinCoordinateNode ._point .length,
         textureCoordinateNode = geometryNode ._texCoord ?.getValue (),
         normalNode            = geometryNode ._normal ?.getValue (),
         coordinateNode        = geometryNode ._coord ?.getValue ();

      if (geometryNode ._coordIndex)
         geometryNode ._coordIndex = geometryNode ._coordIndex .map (index => index < 0 ? -1 : index + start);

      if (geometryNode ._index)
         geometryNode ._index = geometryNode ._index .map (index => index < 0 ? -1 : index + start);

      if (textureCoordinateNode)
      {
         switch (textureCoordinateNode .getType () .at (-1))
         {
            case Base_X3DConstants .TextureCoordinate:
            {
               const
                  skinTextureCoordinateNode = skin .textureCoordinateNode,
                  point                     = skinTextureCoordinateNode ._point;

               textureCoordinateNode ._point .forEach ((p, i) => point [i + start] = p);
               geometryNode ._texCoord = skinTextureCoordinateNode;

               break;
            }
            case Base_X3DConstants .MultiTextureCoordinate:
            {
               const skinMultiTextureCoordinateNode = skin .multiTextureCoordinateNode;

               for (const t of textureCoordinateNode ._texCoord)
               {
                  let s = skinMultiTextureCoordinateNode ._texCoord .find (s => s .mapping === t .mapping) ?.getValue ();

                  if (!s)
                  {
                     if (t .mapping === "TEXCOORD_0")
                     {
                        s = skin .textureCoordinateNode;
                     }
                     else
                     {
                        s           = this .getScene () .createNode ("TextureCoordinate", false);
                        s ._mapping = t .mapping;

                        s .setup ();
                     }

                     skinMultiTextureCoordinateNode ._texCoord .push (s);
                  }

                  const point = s ._point;

                  t .point .forEach ((p, i) => point [i + start] = p);
               }

               geometryNode ._texCoord = skinMultiTextureCoordinateNode;
               break;
            }
         }
      }

      if (normalNode)
      {
         const
            skinNormalNode = skin .normalNode,
            vector         = skinNormalNode ._vector;

         normalNode ._vector .forEach ((v, i) => vector [i + start] = v);
         geometryNode ._normal = skinNormalNode;
      }

      if (coordinateNode)
      {
         const point = skinCoordinateNode ._point;
         coordinateNode ._point .forEach ((p, i) => point [i + start] = p);
         geometryNode ._coord = skinCoordinateNode;
      }
   },
   createInterpolator (timeSensorNode, node, target, interpolation, times, keyValues, cycleInterval)
   {
      const scene = this .getScene ();

      switch (target .path)
      {
         case "pointer":
         {
            const [node, field] = this .getAnimationPointer (target .extensions ?.KHR_animation_pointer ?.pointer);

            return this .createAnimationPointerInterpolator (timeSensorNode, node, field, interpolation, times, keyValues, cycleInterval);
         }
         case "translation":
         case "rotation":
         case "scale":
         {
            const field = node .getField (target .path);

            return this .createAnimationPointerInterpolator (timeSensorNode, node, field, interpolation, times, keyValues, cycleInterval);
         }
         case "weights":
         {
            const
               node              = this .nodes [target .node],
               mesh              = this .meshes [node .mesh],
               primitives        = mesh ?.primitives,
               interpolatorNodes = [ ];

            if (!(primitives instanceof Array))
               return null;

            for (const { shapeNode, targets, attributes } of primitives)
            {
               const geometryNode = shapeNode ._geometry .getValue ();

               if (!geometryNode || !geometryNode ._coord .getValue ())
                  continue;

               if (attributes ["POSITION"] ?.field .length !== geometryNode ._coord .point .length)
                  continue;

               const coordinateInterpolatorNode = this .createArrayInterpolator ("CoordinateInterpolator", interpolation, times, keyValues .array, cycleInterval, targets, attributes, "POSITION");

               if (!coordinateInterpolatorNode)
                  continue;

               if (coordinateInterpolatorNode ._key .length < 2)
                  continue;

               if (coordinateInterpolatorNode ._keyValue .length / coordinateInterpolatorNode ._key .length !== geometryNode ._coord .point .length)
                  continue;

               interpolatorNodes .push (coordinateInterpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", coordinateInterpolatorNode, "set_fraction");
               scene .addRoute (coordinateInterpolatorNode, "value_changed", geometryNode ._coord, "point");
            }

            for (const { shapeNode, targets, attributes } of primitives)
            {
               const geometryNode = shapeNode ._geometry .getValue ();

               if (!geometryNode || !geometryNode ._normal .getValue ())
                  continue;

               if (attributes ["NORMAL"] ?.field .length !== geometryNode ._normal .vector .length)
                  continue;

               const normalInterpolatorNode = this .createArrayInterpolator ("NormalInterpolator", interpolation, times, keyValues .array, cycleInterval, targets, attributes, "NORMAL");

               if (!normalInterpolatorNode)
                  continue;

               if (normalInterpolatorNode ._key .length < 2)
                  continue;

               if (normalInterpolatorNode ._keyValue .length / normalInterpolatorNode ._key .length !== geometryNode ._normal .vector .length)
                  continue;

               interpolatorNodes .push (normalInterpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", normalInterpolatorNode, "set_fraction");
               scene .addRoute (normalInterpolatorNode, "value_changed", geometryNode ._normal, "vector");
            }

            return interpolatorNodes;
         }
         default:
         {
            return [ ];
         }
      }
   },
   createAnimationPointerInterpolator: (() =>
   {
      const interpolators = new Map ([
         [Base_X3DConstants .SFBool,  { typeName: "BooleanSequencer" }],
         [Base_X3DConstants .SFInt32, { typeName: "IntegerSequencer" }],
         [Base_X3DConstants .SFFloat, { typeName: "ScalarInterpolator",     components: 1 }],
         [Base_X3DConstants .SFVec2f, { typeName: "PositionInterpolator2D", components: 2 }],
         [Base_X3DConstants .SFVec3f, { typeName: "PositionInterpolator",   components: 3 }],
      ]);

      return function (timeSensorNode, node, field, interpolation, times, keyValues, cycleInterval)
      {
         if (!(node && field))
            return [ ];

         const scene = this .getScene ();

         switch (field .getType ())
         {
            case Base_X3DConstants .SFColor:
            {
               const interpolatorNodes = [ ];

               let colors, transparencies;

               switch ((keyValues .array .length / times .length) % 3)
               {
                  case 0: // Color3 pointer
                  {
                     colors = keyValues .array;
                     break;
                  }
                  default: // Color4 pointer
                  {
                     colors         = keyValues .array .filter ((_, i) => i % 4 < 3),
                     transparencies = keyValues .array .filter ((_, i) => i % 4 === 3);

                     transparencies = transparencies .every (value => value >= 1)
                        ? undefined
                        : transparencies .map (value => 1 - value);

                     break;
                  }
               }

               const interpolatorNode = this .createNamedInterpolator ("ColorInterpolator", 3, interpolation, times, colors, cycleInterval);

               scene .addNamedNode (scene .getUniqueName (`${GLTF2Parser_$.toUpperCaseFirst (field .getName ())}Interpolator`), interpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
               scene .addRoute (interpolatorNode, "value_changed", node, field .getName ());

               interpolatorNodes .push (interpolatorNode);

               // These are currently the only two affected fields, which are SFColor but pointer is Color4.
               if (field .getName () .match (/^(?:baseColor|emissiveColor)$/) && transparencies)
               {
                  const interpolatorNode = this .createNamedInterpolator ("ScalarInterpolator", 1, interpolation, times, transparencies, cycleInterval);

                  scene .addNamedNode (scene .getUniqueName ("TransparencyInterpolator"), interpolatorNode);

                  scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
                  scene .addRoute (interpolatorNode, "value_changed", node, "transparency");

                  interpolatorNodes .push (interpolatorNode);
               }

               return interpolatorNodes;
            }
            case Base_X3DConstants .SFColorRGBA:
            {
               const interpolatorNodes = [ ];

               let
                  colors         = keyValues .array .filter ((_, i) => i % 4 < 3),
                  transparencies = keyValues .array .filter ((_, i) => i % 4 === 3);

               transparencies = transparencies .every (value => value >= 1)
                  ? undefined
                  : transparencies .map (value => 1 - value);

               // Script

               const scriptNode = scene .createNode ("Script", false);

               scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "color",         new x_ite_Fields .SFColor (1, 1, 1));
               scriptNode .addUserDefinedField (Base_X3DConstants .inputOutput, "alpha" ,        new x_ite_Fields .SFFloat (1));
               scriptNode .addUserDefinedField (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFColorRGBA ());

               scriptNode ._url = [/* js */ `ecmascript:

function eventsProcessed ()
{
   value_changed [0] = color [0];
   value_changed [1] = color [1];
   value_changed [2] = color [2];
   value_changed [3] = alpha;
}
   `];

               scriptNode .setup ();

               scene .addNamedNode (scene .getUniqueName ("CombineColorRGBAScript"), scriptNode);
               scene .addRoute (scriptNode, "value_changed", node, field .getName ());

               // ColorInterpolator

               const interpolatorNode = this .createNamedInterpolator ("ColorInterpolator", 3, interpolation, times, colors, cycleInterval);

               scene .addNamedNode (scene .getUniqueName (`${GLTF2Parser_$.toUpperCaseFirst (field .getName ())}Interpolator`), interpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
               scene .addRoute (interpolatorNode, "value_changed", scriptNode, "color");

               interpolatorNodes .push (interpolatorNode);

               // AlphaInterpolator

               if (transparencies)
               {
                  const interpolatorNode = this .createNamedInterpolator ("ScalarInterpolator", 1, interpolation, times, transparencies, cycleInterval);

                  scene .addNamedNode (scene .getUniqueName ("AlphaInterpolator"), interpolatorNode);

                  scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
                  scene .addRoute (interpolatorNode, "value_changed", scriptNode, "alpha");

                  interpolatorNodes .push (interpolatorNode);
               }

               interpolatorNodes .push (scriptNode);

               return interpolatorNodes;
            }
            case Base_X3DConstants .SFRotation:
            {
               const interpolatorNode = this .createOrientationInterpolator (interpolation, times, keyValues .array, cycleInterval);

               scene .addNamedNode (scene .getUniqueName (`${GLTF2Parser_$.toUpperCaseFirst (field .getName ())}Interpolator`), interpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
               scene .addRoute (interpolatorNode, "value_changed", node, field .getName ());

               return interpolatorNode;
            }
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .SFInt32:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFVec2f:
            case Base_X3DConstants .SFVec3f:
            {
               const
                  { typeName, components} = interpolators .get (field .getType ()),
                  suffix                  = typeName .replace (/^.*?(Sequencer|Interpolator).*?$/, "$1");

               const interpolatorNode = this .createNamedInterpolator (typeName, components, components ? interpolation : "LINEAR", times, keyValues .array, cycleInterval);

               scene .addNamedNode (scene .getUniqueName (GLTF2Parser_$.toUpperCaseFirst (field .getName ()) + suffix), interpolatorNode);

               scene .addRoute (timeSensorNode, "fraction_changed", interpolatorNode, "set_fraction");
               scene .addRoute (interpolatorNode, "value_changed", node, field .getName ());

               return interpolatorNode;
            }
            default:
            {
               return [ ];
            }
         }
      };
   })(),
   getAnimationPointer (pointer = "")
   {
      const
         path  = pointer .split ("/") .filter (p => p),
         field = path .pop () .replace (/(?:Factor$)/, "");

      let glTF = this .input;

      for (const property of path)
         glTF = glTF ?.[property];

      return glTF ?.pointers
         ?.map (node => [node, GLTF2Parser_$.try (() => node ?.getField (this .getAnimationPointerAlias (node, field) ?? field))])
         ?.find (([node, field]) => field)
         ?? [ ];
   },
   addAnimationPointerAlias (node, field, alias)
   {
      const key = `${node .getTypeName ()}.${field}`;

      this .pointerAliases .set (key, alias);
   },
   getAnimationPointerAlias (node, field)
   {
      const key = `${node .getTypeName ()}.${field}`;

      return this .pointerAliases .get (key);
   },
   createNamedInterpolator (typeName, components, interpolation, times, keyValues, cycleInterval)
   {
      const
         scene            = this .getScene (),
         interpolatorNode = scene .createNode (typeName, false);

      switch (interpolation)
      {
         case "STEP":
         {
            const
               key      = [ ],
               keyValue = [ ];

            // Key

            key .push (times [0] / cycleInterval);

            for (let i = 1, length = times .length; i < length; ++ i)
               key .push (times [i] / cycleInterval, times [i] / cycleInterval);

            // KeyValue

            const components2 = components * 2;

            for (let c = 0; c < components; ++ c)
               keyValue .push (keyValues [c]);

            for (let i = 0, length = keyValues .length - components; i < length; i += components)
            {
               for (let c = 0; c < components2; ++ c)
                  keyValue .push (keyValues [i + c]);
            }

            // Finish

            interpolatorNode ._key      = key;
            interpolatorNode ._keyValue = keyValue;

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         default:
         case "LINEAR":
         {
            interpolatorNode ._key      = times .map (t => t / cycleInterval);
            interpolatorNode ._keyValue = keyValues;

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         case "CUBICSPLINE":
         {
            const
               key      = [ ],
               keyValue = [ ],
               vectors  = [ ],
               Vector   = [Numbers_Vector2, Numbers_Vector2, Numbers_Vector3] [components - 1];

            for (let i = 0, length = keyValues .length; i < length; i += components)
            {
               vectors .push (new Vector (... keyValues .subarray (i, i + components)));
            }

            const
               length  = Math .floor (times .at (-1) * SAMPLES_PER_SECOND),
               samples = Array .from ({ length: length }, (_, i) => i / (length - 1) * times .at (-1))

            for (const t of samples)
            {
               key      .push (t / cycleInterval);
               keyValue .push (... this .cubicSplineVector (t, times, vectors));
            }

            // Finish

            interpolatorNode ._key      = key;
            interpolatorNode ._keyValue = components === 1 ? keyValue .filter ((_, i) => i % 2 < 1) : keyValue;

            interpolatorNode .setup ();

            return interpolatorNode;
         }
      }
   },
   createOrientationInterpolator (interpolation, times, keyValues, cycleInterval)
   {
      const
         scene            = this .getScene (),
         interpolatorNode = scene .createNode ("OrientationInterpolator", false);

      switch (interpolation)
      {
         case "STEP":
         {
            // Key

            interpolatorNode ._key .push (times [0] / cycleInterval);

            for (let i = 1, length = times .length; i < length; ++ i)
               interpolatorNode ._key .push (times [i] / cycleInterval, times [i] / cycleInterval);

            // KeyValue

            interpolatorNode ._keyValue .push (Numbers_Rotation4 .fromQuaternion (new Numbers_Quaternion (keyValues [0],
                                                                                          keyValues [1],
                                                                                          keyValues [2],
                                                                                          keyValues [3])));

            for (let i = 0, length = keyValues .length - 4; i < length; i += 4)
            {
               interpolatorNode ._keyValue .push (Numbers_Rotation4 .fromQuaternion (new Numbers_Quaternion (keyValues [i + 0],
                                                                                             keyValues [i + 1],
                                                                                             keyValues [i + 2],
                                                                                             keyValues [i + 3])),
                                                  Numbers_Rotation4 .fromQuaternion (new Numbers_Quaternion (keyValues [i + 4],
                                                                                             keyValues [i + 5],
                                                                                             keyValues [i + 6],
                                                                                             keyValues [i + 7])));
            }

            // Finish

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         default:
         case "LINEAR":
         {
            interpolatorNode ._key = times .map (t => t / cycleInterval);

            for (let i = 0, length = keyValues .length; i < length; i += 4)
            {
               interpolatorNode ._keyValue .push (Numbers_Rotation4 .fromQuaternion (new Numbers_Quaternion (keyValues [i + 0],
                                                                                             keyValues [i + 1],
                                                                                             keyValues [i + 2],
                                                                                             keyValues [i + 3])));
            }

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         case "CUBICSPLINE":
         {
            const quaternions = [ ];

            for (let i = 0, length = keyValues .length; i < length; i += 4)
            {
               quaternions .push (new Numbers_Quaternion (keyValues [i + 0],
                                                  keyValues [i + 1],
                                                  keyValues [i + 2],
                                                  keyValues [i + 3]));
            }

            const
               length  = Math .floor (times .at (-1) * SAMPLES_PER_SECOND),
               samples = Array .from ({ length: length }, (_, i) => i / (length - 1) * times .at (-1))

            for (const t of samples)
            {
               const q = this .cubicSplineVector (t, times, quaternions) .normalize ();

               interpolatorNode ._key      .push (t / cycleInterval);
               interpolatorNode ._keyValue .push (Numbers_Rotation4 .fromQuaternion (q));
            }

            interpolatorNode .setup ();

            return interpolatorNode;
         }
      }
   },
   createArrayInterpolator (typeName, interpolation, times, weights, cycleInterval, targets, accessors, key)
   {
      const accessor = accessors [key];

      if (!accessor)
         return null;

      const
         scene            = this .getScene (),
         interpolatorNode = scene .createNode (typeName, false);

      switch (interpolation)
      {
         case "STEP":
         {
            // Key

            interpolatorNode ._key .push (times [0] / cycleInterval);

            for (let i = 1, length = times .length; i < length; ++ i)
               interpolatorNode ._key .push (times [i] / cycleInterval, times [i] / cycleInterval);

            // KeyValue

            const w = Array .from (targets .keys (), i => weights [i]);

            for (const value of this .applyMorphTargets (accessor .field, targets, key, w))
               interpolatorNode ._keyValue .push (value);

            for (let t = 1, length = times .length; t < length; ++ t)
            {
               const
                  w      = Array .from (targets .keys (), i => weights [t * targets .length + i]),
                  values = this .applyMorphTargets (accessor .field, targets, key, w);

               for (const value of values)
                  interpolatorNode ._keyValue .push (value);

               for (const value of values)
                  interpolatorNode ._keyValue .push (value);
            }

            // Finish

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         default:
         case "LINEAR":
         {
            // Key

            interpolatorNode ._key = times .map (t => t / cycleInterval);

            // KeyValue

            for (const t of times .keys ())
            {
               const w = Array .from (targets .keys (), i => weights [t * targets .length + i]);

               for (const value of this .applyMorphTargets (accessor .field, targets, key, w))
                  interpolatorNode ._keyValue .push (value);
            }

            // Finish

            interpolatorNode .setup ();

            return interpolatorNode;
         }
         case "CUBICSPLINE":
         {
            // Key

            const
               length  = Math .floor (times .at (-1) * SAMPLES_PER_SECOND),
               samples = Array .from ({ length: length }, (_, i) => i / (length - 1) * times .at (-1))

            // KeyValue

            for (const t of samples)
            {
               interpolatorNode ._key .push (t / cycleInterval);

               const w = Array .from (targets .keys (), i => this .cubicSplineScalarArray (t, times, weights, targets .length, i));

               for (const value of this .applyMorphTargets (accessor .field, targets, key, w))
                  interpolatorNode ._keyValue .push (value);
            }

            // Finish

            interpolatorNode .setup ();

            return interpolatorNode;
         }
      }
   },
   applyMorphTargets: (() =>
   {
      const value = new Numbers_Vector3 ();

      return function (array, targets, key, weights)
      {
         const vectors = Array .from (array, v => v .getValue () .copy ());

         for (const [i, target] of targets .entries ())
         {
            const weight = weights [i];

            if (!weight)
               continue;

            const accessor = this .accessors [target [key]];

            if (accessor ?.type !== "VEC3")
               continue;

            const
               array  = accessor .array,
               length = array .length;

            for (let a = 0, p = 0; a < length; a += 3, ++ p)
               vectors [p] .add (value .set (array [a], array [a + 1], array [a + 2]) .multiply (weight));
         }

         return vectors;
      };
   })(),
   cubicSplineVector (time, times, values)
   {
      const
         index1 = Math_Algorithm .clamp (Math_Algorithm .upperBound (times, 0, times .length, time), 1, times .length - 1),
         index0 = index1 - 1,
         td     = times [index1] - times [index0],
         t1     = (time - times [index0]) / td,
         t2     = t1 * t1,
         t3     = t2 * t1,
         v0     = values [index0 * 3 + 1] .copy (),
         b0     = values [index0 * 3 + 2] .copy (),
         v1     = values [index1 * 3 + 1] .copy (),
         a1     = values [index1 * 3 + 0] .copy ();

      v0 .multiply (2 * t3 - 3 * t2 + 1);
      b0 .multiply (td * (t3 - 2 * t2 + t1));
      v1 .multiply (-2 * t3 + 3 * t2);
      a1 .multiply (td * (t3 - t2));

      return v0 .add (b0) .add (v1) .add (a1);
   },
   cubicSplineScalarArray (time, times, values, length, i)
   {
      const
         index1 = Math_Algorithm .clamp (Math_Algorithm .upperBound (times, 0, times .length, time), 1, times .length - 1),
         index0 = index1 - 1,
         td     = times [index1] - times [index0],
         t1     = (time - times [index0]) / td,
         t2     = t1 * t1,
         t3     = t2 * t1;

      let
         v0 = values [(index0 + 1) * length + i],
         b0 = values [(index0 + 2) * length + i],
         v1 = values [(index1 + 1) * length + i],
         a1 = values [(index1 + 0) * length + i];

      v0 *= 2 * t3 - 3 * t2 + 1;
      b0 *= td * (t3 - 2 * t2 + t1);
      v1 *= -2 * t3 + 3 * t2;
      a1 *= td * (t3 - t2);

      return v0 + b0 + v1 + a1;
   },
   vectorValue (array, vector)
   {
      if (!(array instanceof Array))
         return false;

      if (array .length !== vector .length)
         return false;

      vector .set (... array);

      return true;
   },
   numberValue (value, defaultValue)
   {
      if (typeof value !== "number")
         return defaultValue;

      return value;
   },
   stringValue (value, defaultValue)
   {
      if (typeof value !== "string")
         return defaultValue;

      return value;
   },
   description (string)
   {
      return string ?.replace (/_+/g, " ") .trim () ?? "";
   },
});

const GLTF2Parser_default_ = GLTF2Parser;
;

/* harmony default export */ const Parser_GLTF2Parser = (Namespace/* default */.A .add ("GLTF2Parser", GLTF2Parser_default_));
;// ./src/x_ite/Parser/GLB2Parser.js
/* provided dependency */ var GLB2Parser_$ = __webpack_require__(44).A;



function GLB2Parser (scene)
{
   Parser_X3DParser .call (this, scene);

   this .json    = [ ];
   this .buffers = [ ];
}

Object .assign (Object .setPrototypeOf (GLB2Parser .prototype, Parser_X3DParser .prototype),
{
   getEncoding ()
   {
      return "ARRAY_BUFFER";
   },
   setInput (input)
   {
      this .arrayBuffer = input;
      this .dataView    = new DataView (input);
   },
   isValid ()
   {
      if (!(this .arrayBuffer instanceof ArrayBuffer))
         return false;

      const { dataView } = this;

      if (dataView .byteLength < 12)
         return false;

      if (dataView .getUint32 (0, true) !== 0x46546C67) // glTF
         return false;

      if (dataView .getUint32 (4, true) !== 2) // Version
         return false;

      if (dataView .getUint32 (8, true) !== dataView .byteLength)
         return false;

      return true;
   },
   parseIntoScene (resolve, reject)
   {
      this .glb ()
         .then (resolve)
         .catch (reject);
   },
   async glb ()
   {
      this .chunks ();

      const parser = new Parser_GLTF2Parser (this .getScene ());

      parser .setBuffers (this .buffers);

      for (const json of this .json)
      {
         parser .setInput (json);

         if (!parser .isValid ())
            continue;

         await parser .rootObject (parser .input);
      }

      return this .getScene ();
   },
   chunks ()
   {
      const { dataView, arrayBuffer, json, buffers } = this;

      for (let i = 12; i < dataView .byteLength;)
      {
         const
            length = dataView .getUint32 (i, true),
            type   = dataView .getUint32 (i + 4, true);

         i += 8;

         switch (type)
         {
            case 0x4e4f534a: // Structured JSON content
            {
               json .push (GLB2Parser_$.decodeText (arrayBuffer .slice (i, i + length)));
               break;
            }
            case 0x004e4942: // Binary buffer
            {
               buffers .push (arrayBuffer .slice (i, i + length));
               break;
            }
         }

         i += length;
      }
   },
});

const GLB2Parser_default_ = GLB2Parser;
;

/* harmony default export */ const Parser_GLB2Parser = (Namespace/* default */.A .add ("GLB2Parser", GLB2Parser_default_));
;// ./src/x_ite/Parser/OBJParser.js
/* provided dependency */ var OBJParser_$ = __webpack_require__(44).A;







// http://paulbourke.net/dataformats/obj/
// https://people.sc.fsu.edu/~jburkardt/data/obj/obj.html

/*
 *  Grammar
 */

// Lexical elements
const OBJParser_Grammar = Parser_Expressions ({
   // General
   whitespaces: /[\x20\n\t\r,]+/y,
   whitespacesNoLineTerminator: /[\x20\t]+/y,
   comment: /#[^\r\n]*(?=[\r\n]|$)/y,
   untilEndOfLine: /[^\r\n]+/y,

   // Keywords
   mtllib: /\bmtllib\b/y,
   usemtl: /\busemtl\b/y,
   newmtl: /\bnewmtl\b/y,
   Ka: /\bKa\b/y,
   Kd: /\bKd\b/y,
   Ks: /\bKs\b/y,
   Ns: /\bNs\b/y,
   d: /\bd\b/y,
   Tr: /\bTr\b/y,
   illum: /\billum\b/y,
   map_Kd: /\bmap_Kd\b/y,
   o: /\bo\b/y,
   v: /\bv\b/y,
   vt: /\bvt\b/y,
   vn: /\bvn\b/y,
   g: /\bg\b/y,
   s: /\bs\b/y,
   off: /\boff\b/y,
   f: /\bf\b/y,
   slash: /\//y,

   // Values
   int32:  /(?:0[xX][\da-fA-F]+)|(?:[+-]?\d+)/y,
   double: /[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+(?:\.)?))(?:[eE][+-]?\d+)?)/y,
   constants: /([+-])((?:NAN|INF|INFINITY))/iy,
});

/*
 * Parser
 */

function OBJParser (scene)
{
   Parser_X3DParser    .call (this, scene);
   Parser_X3DOptimizer .call (this);

   // Optimizer

   this .removeEmptyGroups    = true;
   this .combineGroupingNodes = false;

   // Globals

   this .geometryIndices = new Map ();
   this .smoothingGroup  = -1;
   this .smoothingGroups = new Map ();
   this .groups          = new Map ();
   this .materials       = new Map ();
   this .textures        = new Map ();
   this .lastIndex       = 0;
}

Object .assign (Object .setPrototypeOf (OBJParser .prototype, Parser_X3DParser .prototype),
   Parser_X3DOptimizer .prototype,
{
   CONSTANTS: new Map ([
      ["NAN", NaN],
      ["INF", Infinity],
      ["INFINITY", Infinity],
   ]),
   getEncoding ()
   {
      return "STRING";
   },
   setInput (string)
   {
      this .input = string;
   },
   isValid ()
   {
      if (typeof this .input !== "string")
         return false;

      return !! this .input .match (/^(?:[\x20\n\t\r]|#[^\r\n]*[\r\n])*\b(?:mtllib|usemtl|o|g|s|vt|vn|v|f)\b/);
   },
   parseIntoScene (resolve, reject)
   {
      this .obj ()
         .then (resolve)
         .catch (reject);
   },
   async obj ()
   {
      // Set profile and components.

      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("OBJ");
      scene .setProfile (browser .getProfile ("Interchange"));

      await browser .loadComponents (scene);

      // Init nodes.

      this .object   = scene .createNode ("Group");
      this .group    = scene .createNode ("Group");
      this .material = this .createDefaultMaterial ();
      this .color    = scene .createNode ("Color");
      this .texCoord = scene .createNode ("TextureCoordinate");
      this .normal   = scene .createNode ("Normal");
      this .coord    = scene .createNode ("Coordinate");

      this .colors    = [ ];
      this .texCoords = [ ];
      this .normals   = [ ];
      this .vertices  = [ ];

      this .object .children .push (this .group);

      scene .getRootNodes () .push (this .object);

      // Parse scene.

      await this .statements ();

      // Assign indices and points.

      for (const [geometry, indices] of this .geometryIndices)
      {
         geometry .texCoordIndex = indices .texCoordIndex;
         geometry .normalIndex   = indices .normalIndex;
         geometry .coordIndex    = indices .coordIndex;
      }

      this .color    .color  = this .colors;
      this .texCoord .point  = this .texCoords;
      this .normal   .vector = this .normals;
      this .coord    .point  = this .vertices;

      // Finish scene.

      this .optimizeSceneGraph (scene .getRootNodes ());

      return this .getScene ();
   },
   comments ()
   {
      while (this .comment ())
         ;
   },
   comment ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .comment .parse (this))
         return true;

      return false;
   },
   whitespaces ()
   {
      OBJParser_Grammar .whitespaces .parse (this);
   },
   whitespacesNoLineTerminator ()
   {
      OBJParser_Grammar .whitespacesNoLineTerminator .parse (this);
   },
   async statements ()
   {
      while (await this .statement ())
         ;
   },
   async statement ()
   {
      if (await this .mtllibs ())
         return true;

      if (this .usemtl ())
         return true;

      if (this .o ())
         return true;

      if (this .g ())
         return true;

      if (this .s ())
         return true;

      if (this .vts ())
         return true;

      if (this .vns ())
         return true;

      if (this .vs ())
         return true;

      if (this .fs ())
         return true;

      // Skip empty and unknown lines.

      if (OBJParser_Grammar .untilEndOfLine .parse (this))
         return true;

      return false;
   },
   async mtllibs ()
   {
      this .comments ();

      if (OBJParser_Grammar .mtllib .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            const mtllibs = this .result [0] .trim () .split (/\s+/);

            await Promise .all (mtllibs .map (path => this .mtllib (path)));
         }

         return true;
      }

      return false;
   },
   createDefaultMaterial (id)
   {
      const
         scene    = this .getExecutionContext (),
         material = scene .createNode ("Material");

      this .materials .set (id, material);

      return material;
   },
   async mtllib (path)
   {
      try
      {
         const
            scene    = this .getExecutionContext (),
            url      = new URL (path, scene .getBaseURL ()),
            response = await fetch (url),
            blob     = await response .blob (),
            input    = OBJParser_$.decodeText (await OBJParser_$.gunzip (blob)),
            parser   = new MaterialParser (scene, input);

         parser .parse ();

         for (const [id, material] of parser .materials)
            this .materials .set (id, material);

         for (const [id, texture] of parser .textures)
            this .textures .set (id, texture);
      }
      catch (error)
      {
         console .warn (error);
      }
   },
   usemtl ()
   {
      this .comments ();

      if (OBJParser_Grammar .usemtl .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            const id = this .result [0];

            const
               scene    = this .getExecutionContext (),
               material = this .materials .get (id) ?? this .createDefaultMaterial (id),
               texture  = this .textures .get (id),
               name     = this .sanitizeName (id);

            if (name)
            {
               if (!material .getNodeName ())
               {
                  scene .addNamedNode (scene .getUniqueName (name), material);
                  scene .addExportedNode (scene .getUniqueExportName (name), material);
               }

               if (texture && !texture .getNodeName ())
               {
                  scene .addNamedNode (scene .getUniqueName (name), texture);
                  scene .addExportedNode (scene .getUniqueExportName (name), texture);
               }
            }

            this .material = material;
            this .texture  = texture;
         }

         return true;
      }

      return false;
   },
   o ()
   {
      this .comments ();

      if (OBJParser_Grammar .o .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            const
               scene = this .getExecutionContext (),
               name  = this .sanitizeName (this .result [0]);

            if (this .group .children .length)
            {
               this .object = scene .createNode ("Group");
               this .group  = scene .createNode ("Group");

               this .object .children .push (this .group);
               scene .getRootNodes () .push (this .object);
            }

            if (name && !this .object .getNodeName ())
            {
               scene .addNamedNode (scene .getUniqueName (name), this .object);
               scene .addExportedNode (scene .getUniqueExportName (name), this .object);
            }
         }

         return true;
      }

      return false;
   },
   g ()
   {
      this .comments ();

      if (OBJParser_Grammar .g .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            const
               scene = this .getExecutionContext (),
               id    = this .result [0],
               name  = this .sanitizeName (id),
               group = this .groups .get (id);

            if (group)
            {
               this .group = group;
            }
            else
            {
               if (this .group .children .length)
               {
                  this .group = scene .createNode ("Group");

                  this .object .children .push (this .group);
               }
            }

            this .groups .set (id, this .group);

            if (name && !this .group .getNodeName ())
            {
               scene .addNamedNode (scene .getUniqueName (name), this .group);
               scene .addExportedNode (scene .getUniqueExportName (name), this .group);
            }

            this .smoothingGroup = -1;
         }

         return true;
      }

      return false;
   },
   getSmoothingGroupId ()
   {
      let id = "";

      id += this .smoothingGroup;
      id += ".";
      id += this .group .getId ();
      id += ".";
      id += this .material .getId ();
      id += ".";
      id += this .texture ?.getId () ?? "";

      return id;
   },
   s ()
   {
      this .comments ();

      if (OBJParser_Grammar .s .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .off .parse (this))
         {
            this .smoothingGroup = 0;
            return true;
         }

         if (this .int32 ())
         {
            this .smoothingGroup = this .value;
            return true;
         }

         return true;
      }

      return false;
   },
   vts ()
   {
      const texCoords = this .texCoords;

      let result = false;

      while (this .vt (texCoords))
         result = true;

      return result;
   },
   vt (texCoords)
   {
      this .comments ();

      if (OBJParser_Grammar .vt .parse (this))
      {
         if (this .vec2 (texCoords))
            return true;

         throw new Error ("Expected a texture coordinate.");
      }

      return false;
   },
   vns ()
   {
      const normals = this .normals;

      let result = false;

      while (this .vn (normals))
         result = true;

      return result;
   },
   vn (normals)
   {
      this .comments ();

      if (OBJParser_Grammar .vn .parse (this))
      {
         if (this .vec3 (normals))
            return true;

         throw new Error ("Expected a normal vector.");
      }

      return false;
   },
   vs ()
   {
      const
         colors   = this .colors,
         vertices = this .vertices;

      let result = false;

      while (this .v (vertices, colors))
         result = true;

      return result;
   },
   v (vertices, colors)
   {
      this .comments ();

      if (OBJParser_Grammar .v .parse (this))
      {
         if (this .vec3 (vertices))
         {
            this .whitespacesNoLineTerminator ();

            if (this .vec3 (colors))
            {
               if (this .double ()) // RGBA
                  colors .push (this .value);
            }

            return true;
         }

         throw new Error ("Expected a vertex coordinate.");
      }

      return false;
   },
   fs ()
   {
      this .comments ();

      if (OBJParser_Grammar .f .lookahead (this))
      {
         const shape = this .smoothingGroups .get (this .getSmoothingGroupId ());

         if (shape)
         {
            this .shape    = shape;
            this .geometry = this .shape .geometry;

            const indices = this .geometryIndices .get (this .geometry);

            this .texCoordIndex = indices .texCoordIndex;
            this .normalIndex   = indices .normalIndex;
            this .coordIndex    = indices .coordIndex;
         }
         else
         {
            const
               scene      = this .getExecutionContext (),
               appearance = scene .createNode ("Appearance");

            this .shape         = scene .createNode ("Shape");
            this .geometry      = scene .createNode ("IndexedFaceSet");
            this .texCoordIndex = [ ];
            this .normalIndex   = [ ];
            this .coordIndex    = [ ];

            this .geometryIndices .set (this .geometry,
            {
               texCoordIndex: this .texCoordIndex,
               normalIndex:   this .normalIndex,
               coordIndex:    this .coordIndex,
            });

            appearance .material        = this .material;
            appearance .texture         = this .texture;
            this .geometry .creaseAngle = this .smoothingGroup ? Math .PI : 0;
            this .shape .appearance     = appearance;
            this .shape .geometry       = this .geometry;

            this .group .children .push (this .shape);

            this .smoothingGroups .set (this .getSmoothingGroupId (), this .shape);
         }

         while (this .f ())
            ;

         if (this .colors .length > this .coord .length && this .color .getNodeType () .includes (Base_X3DConstants .Color))
         {
            const scene = this .getExecutionContext ();

            this .color = scene .createNode ("ColorRGBA");

            this .color .color = this .colors;
         }

         if (this .colors .length)
            this .geometry .color = this .color;

         if (this .texCoordIndex .length)
            this .geometry .texCoord = this .texCoord;

         if (this .normalIndex .length)
            this .geometry .normal = this .normal;

         this .geometry .coord = this .coord;

         return true;
      }

      return false;
   },
   f ()
   {
      this .comments ();

      if (OBJParser_Grammar .f .parse (this))
      {
         const
            texCoordIndex      = this .texCoordIndex,
            normalIndex        = this .normalIndex,
            coordIndex         = this .coordIndex,
            numTexCoordIndices = texCoordIndex .length,
            numNormalIndices   = normalIndex .length,
            numTexCoords       = this .texCoords .length,
            numNormals         = this .normals .length,
            numCoords          = this .vertices .length;

         while (this .indices (texCoordIndex, normalIndex, coordIndex, numTexCoords, numNormals, numCoords))
            ;

         if (texCoordIndex .length !== numTexCoordIndices)
            texCoordIndex .push (-1);

         if (normalIndex .length !== numNormalIndices)
            normalIndex .push (-1);

         coordIndex .push (-1);

         return true;
      }

      return false;
   },
   indices (texCoordIndex, normalIndex, coordIndex, numTexCoords, numNormals, numCoords)
   {
      if (this .int32 ())
      {
         coordIndex .push (this .index (this .value, numCoords));

         if (OBJParser_Grammar .slash .parse (this))
         {
            if (this .int32 ())
            {
               texCoordIndex .push (this .index (this .value, numTexCoords));
            }

            if (OBJParser_Grammar .slash .parse (this))
            {
               if (this .int32 ())
               {
                  normalIndex .push (this .index (this .value, numNormals));
               }
            }
         }

         return true;
      }

      return false;
   },
   index (index, length)
   {
      if (index === 0)
         throw new Error ("Invalid index.");

      if (index < 0)
         return length + index;

      return index - 1;
   },
   int32 ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .int32 .parse (this))
      {
         this .value = parseInt (this .result [0]);

         return true;
      }

      return false;
   },
   double ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);

         return true;
      }

      if (OBJParser_Grammar .constants .parse (this))
      {
         this .value = this .CONSTANTS .get (this .result [2] .toUpperCase ());

         if (this .result [1] === "-")
            this .value = - this .value;

         return true;
      }

      return false;
   },
   vec2 (array)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            array .push (x, y);
            return true;
         }
      }

      return false;
   },
   vec3 (array)
   {
      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const z = this .value;

               array .push (x, y, z);
               return true;
            }
         }
      }

      return false;
   },
});

function MaterialParser (scene, input)
{
   this .executionContext = scene;
   this .input            = input;
   this .material         = scene .createNode ("Material");
   this .materials        = new Map ();
   this .textures         = new Map ();
   this .color3           = new Numbers_Color3 ();
   this .id               = "";
}

Object .assign (MaterialParser .prototype,
{
   CONSTANTS: new Map ([
      ["NAN", NaN],
      ["INF", Infinity],
      ["INFINITY", Infinity],
   ]),
   parse ()
   {
      try
      {
         this .statements ();
      }
      catch (error)
      {
         if (DEVELOPMENT)
            console .log (error);
      }
   },
   comments ()
   {
      while (this .comment ())
         ;
   },
   comment ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .comment .parse (this))
         return true;

      return false;
   },
   whitespaces ()
   {
      OBJParser_Grammar .whitespaces .parse (this);
   },
   whitespacesNoLineTerminator ()
   {
      OBJParser_Grammar .whitespacesNoLineTerminator .parse (this);
   },
   statements ()
   {
      while (this .statement ())
         ;
   },
   statement ()
   {
      if (this .newmtl ())
         return true;

      if (this .Ka ())
         return true;

      if (this .Kd ())
         return true;

      if (this .Ks ())
         return true;

      if (this .Ns ())
         return true;

      if (this .d ())
         return true;

      if (this .Tr ())
         return true;

      if (this .illum ())
         return true;

      if (this .map_Kd ())
         return true;

      // Skip empty and unknown lines.

      if (OBJParser_Grammar .untilEndOfLine .parse (this))
         return true;

      return false;
   },
   newmtl ()
   {
      this .comments ();

      if (OBJParser_Grammar .newmtl .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         this .id = "";

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            this .id = this .result [0];

            this .material = this .executionContext .createNode ("Material");

            this .materials .set (this .id, this .material);

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   Ka ()
   {
      this .comments ();

      if (OBJParser_Grammar .Ka .parse (this))
      {
         if (this .col3 ())
         {
            const hsv = this .color3 .getHSV ();

            this .material .ambientIntensity = hsv [2];

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   Kd ()
   {
      this .comments ();

      if (OBJParser_Grammar .Kd .parse (this))
      {
         if (this .col3 ())
         {
            this .material .diffuseColor = this .color3;

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   Ks ()
   {
      this .comments ();

      if (OBJParser_Grammar .Ks .parse (this))
      {
         if (this .col3 ())
         {
            this .material .specularColor = this .color3;

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   Ns ()
   {
      this .comments ();

      if (OBJParser_Grammar .Ns .parse (this))
      {
         if (this .double ())
         {
            this .material .shininess = this .value / 1000;

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   d ()
   {
      this .comments ();

      if (OBJParser_Grammar .d .parse (this))
      {
         if (this .double ())
         {
            this .material .transparency = 1 - this .value;

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   Tr ()
   {
      this .comments ();

      if (OBJParser_Grammar .Tr .parse (this))
      {
         if (this .double ())
         {
            this .material .transparency = this .value;

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   illum ()
   {
      this .comments ();

      if (OBJParser_Grammar .illum .parse (this))
      {
         if (this .int32 ())
         {
            // Don't know what to do with illum value in X3D.
            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   map_Kd ()
   {
      this .comments ();

      if (OBJParser_Grammar .map_Kd .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (OBJParser_Grammar .untilEndOfLine .parse (this))
         {
            const string = this .result [0];

            if (string .length && this .id .length)
            {
               const paths = string .trim () .split (/\s+/);

               if (paths .length)
               {
                  const
                     scene   = this .executionContext,
                     texture = scene .createNode ("ImageTexture"),
                     path    = paths .at (-1) .replace (/\\/g, "/");

                  texture .url = [path];

                  this .textures .set (this .id, texture);
               }
            }

            return true;
         }

         OBJParser_Grammar .untilEndOfLine .parse (this);

         return true;
      }

      return false;
   },
   int32 ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .int32 .parse (this))
      {
         this .value = parseInt (this .result [0]);

         return true;
      }

      return false;
   },
   double ()
   {
      this .whitespaces ();

      if (OBJParser_Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);

         return true;
      }

      if (OBJParser_Grammar .constants .parse (this))
      {
         this .value = this .CONSTANTS .get (this .result [2] .toUpperCase ());

         if (this .result [1] === "-")
            this .value = -this .value;

         return true;
      }

      return false;
   },
   col3 ()
   {
      if (this .double ())
      {
         this .color3 .r = this .value;

         if (this .double ())
         {
            this .color3 .g = this .value;

            if (this .double ())
            {
               this .color3 .b = this .value;

               return true;
            }
         }
      }

      return false;
   },
});

const OBJParser_default_ = OBJParser;
;

/* harmony default export */ const Parser_OBJParser = (Namespace/* default */.A .add ("OBJParser", OBJParser_default_));
;// ./src/x_ite/Parser/STLAParser.js




// http://paulbourke.net/dataformats/stl/
// https://people.sc.fsu.edu/~jburkardt/data/obj/obj.html

/*
 *  Grammar
 */

// Lexical elements
const STLAParser_Grammar = Parser_Expressions ({
   // General
   whitespaces: /[\x20\n\t\r,]+/y,
   whitespacesNoLineTerminator: /[\x20\t]+/y,
   comment: /;[^\r\n]*(?=[\r\n]|$)/y,
   untilEndOfLine: /[^\r\n]+/y,

   // Keywords
   solid: /\bsolid\b/y,
   facet: /\bfacet\b/y,
   normal: /\bnormal\b/y,
   outer: /\bouter\b/y,
   loop: /\bloop\b/y,
   vertex: /\bvertex\b/y,
   endloop: /\bendloop\b/y,
   endfacet: /\bendfacet\b/y,
   endsolid: /\bendsolid\b/y,

   // Values
   name: /\w+/y,
   double: /[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+(?:\.)?))(?:[eE][+-]?\d+)?)/y,
   constants: /([+-])((?:NAN|INF|INFINITY))/iy,
});

/*
 * Parser
 */

function STLAParser (scene)
{
   Parser_X3DParser .call (this, scene);
}

Object .assign (Object .setPrototypeOf (STLAParser .prototype, Parser_X3DParser .prototype),
{
   CONSTANTS: new Map ([
      ["NAN", NaN],
      ["INF", Infinity],
      ["INFINITY", Infinity],
   ]),
   getEncoding ()
   {
      return "STRING";
   },
   setInput (string)
   {
      this .input = string;
   },
   isValid ()
   {
      if (typeof this .input !== "string")
         return false;

      return !! this .input .match (/^(?:[\x20\n\t\r]|;[^\r\n]*[\r\n])*\bsolid\b.*?\bendsolid\b/s);
   },
   parseIntoScene (resolve, reject)
   {
      this .stl ()
         .then (resolve)
         .catch (reject);
   },
   async stl ()
   {
      // Set profile and components.

      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("STL");
      scene .setProfile (browser .getProfile ("Interchange"));

      await browser .loadComponents (scene);

      // Create nodes.

      this .material   = scene .createNode ("Material");
      this .appearance = scene .createNode ("Appearance");

      this .material .diffuseColor = Numbers_Color3 .WHITE;
      this .appearance .material   = this .material;

      // Parse scene.

      this .statements ();

      return this .getScene ();
   },
   comments ()
   {
      while (this .comment ())
         ;
   },
   comment ()
   {
      this .whitespaces ();

      if (STLAParser_Grammar .comment .parse (this))
         return true;

      return false;
   },
   whitespaces ()
   {
      STLAParser_Grammar .whitespaces .parse (this);
   },
   whitespacesNoLineTerminator ()
   {
      STLAParser_Grammar .whitespacesNoLineTerminator .parse (this);
   },
   statements ()
   {
      while (this .solid ())
         ;
   },
   solid ()
   {
      this .comments ();

      if (!STLAParser_Grammar .solid .parse (this))
         return false;

      this .whitespacesNoLineTerminator ();

      const
         scene      = this .getExecutionContext (),
         shape      = scene .createNode ("Shape"),
         geometry   = scene .createNode ("TriangleSet"),
         coordinate = scene .createNode ("Coordinate"),
         hasNormals = this .normals ?.some (v => v !== 0),
         name       = this .sanitizeName (STLAParser_Grammar .name .parse (this) ? this .result [0] : "");

      STLAParser_Grammar .untilEndOfLine .parse (this);

      this .facets ();
      this .rotateAxes90 (this .vertices);

      shape .appearance         = this .appearance;
      shape .geometry           = geometry;
      coordinate .point         = this .vertices;
      geometry .normalPerVertex = false;
      geometry .coord           = coordinate;

      if (hasNormals)
      {
         const normal = scene .createNode ("Normal");

         this .rotateAxes90 (this .normals);

         normal .vector   = this .normals;
         geometry .normal = normal;
      }

      if (name)
      {
         scene .addNamedNode (scene .getUniqueName (name), shape);
         scene .addExportedNode (scene .getUniqueExportName (name), shape);
      }

      scene .getRootNodes () .push (shape);

      this .comments ();

      if (STLAParser_Grammar .endsolid .parse (this))
         return true;

      throw new Error ("Expected 'endsolid' statement.");
   },
   facets ()
   {
      this .normals  = [ ];
      this .vertices = [ ];

      while (this .facet (this .normals, this .vertices))
         ;
   },
   facet (normals, vertices)
   {
      this .comments ()

      if (STLAParser_Grammar .facet .parse (this))
      {
         if (this .normal (normals))
         {
            if (this .loop (vertices))
            {
               this .comments ();

               if (STLAParser_Grammar .endfacet .parse (this))
                  return true;

               throw new Error ("Expected 'endfacet' statement.");
            }
         }
      }

      return false;
   },
   normal (normals)
   {
      this .whitespacesNoLineTerminator ();

      if (STLAParser_Grammar .normal .parse (this))
      {
         if (this .double ())
         {
            const x = this .value;

            if (this .double ())
            {
               const y = this .value;

               if (this .double ())
               {
                  const z = this .value;

                  normals .push (x, y, z);
                  return true;
               }

               throw new Error ("Expected a double.");
            }

            throw new Error ("Expected a double.");
         }

         throw new Error ("Expected a double.");
      }

      throw new Error ("Expected 'normal' statement.");
   },
   loop (vertices)
   {
      this .comments ();

      if (STLAParser_Grammar .outer .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (STLAParser_Grammar .loop .parse (this))
         {
            if (this .vertex (vertices))
            {
               if (this .vertex (vertices))
               {
                  if (this .vertex (vertices))
                  {
                     this .comments ();

                     if (STLAParser_Grammar .endloop .parse (this))
                        return true;

                     throw new Error ("Expected 'endloop' statement.");
                  }
               }
            }
         }

         throw new Error ("Expected 'loop' statement.");
      }

      throw new Error ("Expected 'outer' statement.");
   },
   vertex (vertices)
   {
      this .comments ();

      if (STLAParser_Grammar .vertex .parse (this))
      {
         if (this .double ())
         {
            const x = this .value;

            if (this .double ())
            {
               const y = this .value;

               if (this .double ())
               {
                  const z = this .value;

                  vertices .push (x, y, z);
                  return true;
               }

               throw new Error ("Expected a double.");
            }

            throw new Error ("Expected a double.");
         }

         throw new Error ("Expected a double.");
      }

      throw new Error ("Expected 'vertex' statement.");
   },
   double ()
   {
      this .whitespacesNoLineTerminator ();

      if (STLAParser_Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);

         return true;
      }

      if (STLAParser_Grammar .constants .parse (this))
      {
         this .value = this .CONSTANTS .get (this .result [2] .toUpperCase ());

         if (this .result [1] === "-")
            this .value = - this .value;

         return true;
      }

      return false;
   },
});

const STLAParser_default_ = STLAParser;
;

/* harmony default export */ const Parser_STLAParser = (Namespace/* default */.A .add ("STLAParser", STLAParser_default_));
;// ./src/x_ite/Parser/STLBParser.js
/* provided dependency */ var STLBParser_$ = __webpack_require__(44).A;



// http://paulbourke.net/dataformats/stl/
// https://people.sc.fsu.edu/~jburkardt/data/obj/obj.html

/*
 * Parser
 */

function STLBParser (scene)
{
   Parser_X3DParser .call (this, scene);
}

Object .assign (Object .setPrototypeOf (STLBParser .prototype, Parser_X3DParser .prototype),
{
   getEncoding ()
   {
      return "ARRAY_BUFFER";
   },
   setInput (input)
   {
      this .arrayBuffer = input;
      this .dataView    = new DataView (input);
   },
   isValid ()
   {
      if (!(this .arrayBuffer instanceof ArrayBuffer))
         return false;

      if (this .dataView .byteLength < 84)
         return false;

      const
         numFaces   = this .dataView .getUint32 (80, true),
         byteLength = numFaces * 50 + 84;

      return byteLength === this .dataView .byteLength;
   },
   parseIntoScene (resolve, reject)
   {
      this .stl ()
         .then (resolve)
         .catch (reject);
   },
   async stl ()
   {
      // Set profile and components.

      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("STL");
      scene .setProfile (browser .getProfile ("Interchange"));

      await browser .loadComponents (scene);

      // Create nodes.

      this .material   = scene .createNode ("Material");
      this .appearance = scene .createNode ("Appearance");

      this .material .diffuseColor = Numbers_Color3 .WHITE;
      this .appearance .material   = this .material;

      // Parse scene.

      this .header ();
      this .shape ();

      return this .getScene ();
   },
   header ()
   {
      const header = STLBParser_$.decodeText (this .arrayBuffer .slice (0, 80)) .trim ();

      if (!header)
         return;

      const
         scene     = this .getScene (),
         worldInfo = scene .createNode ("WorldInfo"),
         url       = new URL (scene .worldURL);

      worldInfo .title = url .protocol === "data:" ? "STL Model" : decodeURIComponent (url .pathname .split ('/') .at (-1));
      worldInfo .info  = [header];

      scene .rootNodes .push (worldInfo);
   },
   shape ()
   {
      const
         scene      = this .getExecutionContext (),
         shape      = scene .createNode ("Shape"),
         geometry   = scene .createNode ("TriangleSet"),
         coordinate = scene .createNode ("Coordinate"),
         dataView   = this .dataView,
         byteLength = this .dataView .byteLength,
         normals    = [ ],
         points     = [ ];

      for (let i = 84; i < byteLength; i += 50)
      {
         for (let f = 0; f < 3; ++ f)
            normals .push (dataView .getFloat32 (i + f * 4, true));

         for (let f = 3; f < 12; ++ f)
            points .push (dataView .getFloat32 (i + f * 4, true));
      }

      this .rotateAxes90 (points);

      shape .appearance         = this .appearance;
      shape .geometry           = geometry;
      coordinate .point         = points;
      geometry .normalPerVertex = false;
      geometry .coord           = coordinate;

      if (normals .some (v => v !== 0))
      {
         const normal = scene .createNode ("Normal");

         this .rotateAxes90 (normals);

         normal .vector   = normals;
         geometry .normal = normal;
      }

      scene .getRootNodes () .push (shape);
   },
});

const STLBParser_default_ = STLBParser;
;

/* harmony default export */ const Parser_STLBParser = (Namespace/* default */.A .add ("STLBParser", STLBParser_default_));
;// ./src/x_ite/Parser/PLYAParser.js




/*
 *  Grammar
 */

// Lexical elements
const PLYAParser_Grammar = Parser_Expressions ({
   // General
   whitespaces: /[\x20\n\t\r,]+/y,
   whitespacesNoLineTerminator: /[\x20\t]+/y,
   untilEndOfLine: /[^\r\n]+/y,
   line: /[^\r\n]*\r?\n/y,

   // Keywords
   ply: /ply/y,
   format: /format ascii 1.0/y,
   comment: /\bcomment\b/y,
   element: /\belement\b/y,
   elementName: /\b\S+\b/y,
   property: /\bproperty\b/y,
   propertyList: /\blist\b/y,
   propertyType: /\b(?:char|uchar|short|ushort|int|uint|float|double|int8|uint8|int16|uint16|int32|uint32|float32|float64)\b/y,
   propertyName: /\b\S+\b/y,
   endHeader: /\bend_header\b/y,

   double: /[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+(?:\.)?))(?:[eE][+-]?\d+)?)/y,
   int32:  /(?:0[xX][\da-fA-F]+)|(?:[+-]?\d+)/y,
});

/*
 * Parser
 */

function PLYAParser (scene)
{
   Parser_X3DParser .call (this, scene);

   this .triangles = true;
   this .comments  = [ ];

   this .typeMapping = new Map ([
      ["char",    this .int32],
      ["uchar",   this .int32],
      ["short",   this .int32],
      ["ushort",  this .int32],
      ["int",     this .int32],
      ["uint",    this .int32],
      ["float",   this .double],
      ["double",  this .double],
      ["int8",    this .int32],
      ["uint8",   this .int32],
      ["int16",   this .int32],
      ["uint16",  this .int32],
      ["int32",   this .int32],
      ["uint32",  this .int32],
      ["float32", this .double],
      ["float64", this .double],
   ]);
}

Object .assign (Object .setPrototypeOf (PLYAParser .prototype, Parser_X3DParser .prototype),
{
   getEncoding ()
   {
      return "STRING";
   },
   setInput (input)
   {
      this .input = input;
   },
   isValid ()
   {
      return this .input .match (/^ply\r?\nformat ascii 1.0/);
   },
   parseIntoScene (resolve, reject)
   {
      this .ply ()
         .then (resolve)
         .catch (reject);
   },
   async ply ()
   {
      // Set profile and components.

      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("PLY");
      scene .setProfile (browser .getProfile ("Interchange"));

      await browser .loadComponents (scene);

      await this .processElements (this .header ([ ]));

      // Create nodes.

      return this .getScene ();
   },
   whitespacesOrComments ()
   {
      while (this .whitespaces () || this .comment ())
         ;
   },
   whitespaces ()
   {
      return PLYAParser_Grammar .whitespaces .parse (this);
   },
   whitespacesNoLineTerminator ()
   {
      PLYAParser_Grammar .whitespacesNoLineTerminator .parse (this);
   },
   comment ()
   {
      if (PLYAParser_Grammar .comment .parse (this) && PLYAParser_Grammar .untilEndOfLine .parse (this))
      {
         const value = this .result [0] .trim ();

         this .comments .push (value);

         this .mustRotateAxes ||= !! value .match (/\b(?:Blender|Artec|Polycam)\b/i);

         return true;
      }

      return false;
   },
   header (elements)
   {
      PLYAParser_Grammar .ply .parse (this);
      PLYAParser_Grammar .whitespaces .parse (this);
      PLYAParser_Grammar .format .parse (this);

      this .headings (elements);

      const
         scene     = this .getScene (),
         worldInfo = scene .createNode ("WorldInfo"),
         url       = new URL (scene .worldURL);

      worldInfo .title = url .protocol === "data:" ? "PLY Model" : decodeURIComponent (url .pathname .split ('/') .at (-1));
      worldInfo .info  = this .comments;

      scene .rootNodes .push (worldInfo);

      return elements;
   },
   headings (elements)
   {
      while (this .head (elements))
         ;
   },
   head (elements)
   {
      if (this .element (elements))
         return true;

      if (PLYAParser_Grammar .endHeader .parse (this))
         return false;

      if (PLYAParser_Grammar .untilEndOfLine .parse (this))
         return true;

      return false;
   },
   element (elements)
   {
      this .whitespacesOrComments ();

      if (PLYAParser_Grammar .element .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (PLYAParser_Grammar .elementName .parse (this))
         {
            const name = this .result [0];

            if (this .int32 ())
            {
               const element =
               {
                  name: name,
                  count: this .value,
                  properties: [ ],
               };

               this .properties (element .properties);

               elements .push (element);
               return true;
            }
         }
      }

      return false;
   },
   properties (properties)
   {
      while (this .property (properties))
         ;
   },
   property (properties)
   {
      this .whitespacesOrComments ();

      if (PLYAParser_Grammar .property .parse (this))
      {
         this .whitespacesNoLineTerminator ();

         if (PLYAParser_Grammar .propertyType .parse (this))
         {
            const
               type  = this .result [0],
               value = this .typeMapping .get (type);

            this .whitespacesNoLineTerminator ();

            if (PLYAParser_Grammar .propertyName .parse (this))
            {
               const name = this .result [0];

               properties .push ({ type, value, name });
               return true;
            }
         }

         if (PLYAParser_Grammar .propertyList .parse (this))
         {
            this .whitespacesNoLineTerminator ();

            if (PLYAParser_Grammar .propertyType .parse (this))
            {
               const count = this .typeMapping .get (this .result [0]);

               this .whitespacesNoLineTerminator ();

               if (PLYAParser_Grammar .propertyType .parse (this))
               {
                  const
                     type  = this .result [0],
                     value = this .typeMapping .get (type);

                  this .whitespacesNoLineTerminator ();

                  if (PLYAParser_Grammar .propertyName .parse (this))
                  {
                     const name = this .result [0];

                     properties .push ({ count, type, value, name });
                     return true;
                  }
               }
            }
         }
      }

      return false;
   },
   async processElements (elements)
   {
      for (const element of elements)
         this .processElement (element);

      if (!this .points ?.length)
         return;

      const scene = this .getScene ();

      if (this .sphericalHarmonics)
      {
         scene .addComponent (this .getBrowser () .getComponent ("GaussianSplats"));

         await this .getBrowser () .loadComponents (scene);

         const
            transform      = scene .createNode ("Transform"),
            gaussianSplats = scene .createNode ("GaussianSplats"),
            quaternions    = this .quaternions,
            numQuaternions = quaternions .length,
            orientations   = [ ];

         // Quaternion elements must be rotated from wxyz to xyzw.
         // https://www.kaggle.com/code/stpeteishii/creatures-ply-to-gaussian-splat
         for (let i = 0; i < numQuaternions; i += 4)
            orientations .push (quaternions [i + 1], quaternions [i + 2], quaternions [i + 3], quaternions [i]);

         gaussianSplats .positions    = this .points;
         gaussianSplats .orientations = orientations;
         gaussianSplats .scales       = this .scales;
         gaussianSplats .opacities    = this .opacities;

         // Degrees 0,1,2,3

         let i = 0;

         for (const [degree, coefs] of [1, 3, 5, 7] .entries ())
         {
            for (let coef = 0; coef < coefs; ++ coef)
               gaussianSplats [`sphericalHarmonicsDegree${degree}Coef${coef}`] = this .sphericalHarmonics [i ++];
         }

         transform .rotation = new Numbers_Rotation4 (1, 0, 0, Math .PI);
         transform .children .push (gaussianSplats);

         scene .rootNodes .push (transform);
      }
      else if (this .coordIndex) // IndexedFaceSet
      {
         const
            hasNormals = this .normals ?.some (v => v !== 0),
            triangles  = this .triangles && !this .texCoordIndex ?.length,
            shape      = scene .createNode ("Shape"),
            appearance = scene .createNode ("Appearance"),
            material   = scene .createNode ("Material"),
            geometry   = scene .createNode (triangles ? "IndexedTriangleSet" : "IndexedFaceSet"),
            coordinate = scene .createNode ("Coordinate");

         if (triangles)
            geometry .index = this .coordIndex .filter (v => v !== -1);
         else
            geometry .coordIndex = this .coordIndex;

         geometry .color = this .createColor ();

         if (this .texCoords ?.length)
         {
            // TextureTransform

            const textureTransform = scene .createNode ("TextureTransform");

            textureTransform .translation .y = -1;
            textureTransform .scale .y       = -1;

            appearance .textureTransform = textureTransform;

            // TextureCoordinate

            const texCoord = scene .createNode ("TextureCoordinate");

            texCoord .point    = this .texCoords;
            geometry .texCoord = texCoord;

            // Index

            if (this .texCoordIndex ?.length)
               geometry .texCoordIndex = this .texCoordIndex;
         }

         if (hasNormals)
         {
            const normal = scene .createNode ("Normal");

            if (this .mustRotateAxes)
               this .rotateAxes90 (this .normals);

            normal .vector   = this .normals;
            geometry .normal = normal;
         }

         if (this .mustRotateAxes)
            this .rotateAxes90 (this .points);

         coordinate .point = this .points;
         geometry .coord   = coordinate;

         appearance .material = material;
         shape .appearance    = appearance;
         shape .geometry      = geometry;

         scene .getRootNodes () .push (shape);
      }
      else // PointSet
      {
         const
            hasNormals = this .normals ?.some (v => v !== 0),
            shape      = scene .createNode ("Shape"),
            appearance = scene .createNode ("Appearance"),
            material   = scene .createNode (hasNormals ? "Material" : "UnlitMaterial"),
            geometry   = scene .createNode ("PointSet"),
            coordinate = scene .createNode ("Coordinate");

         geometry .color = this .createColor ();

         if (hasNormals)
         {
            const normal = scene .createNode ("Normal");

            if (this .mustRotateAxes)
               this .rotateAxes90 (this .normals);

            normal .vector   = this .normals;
            geometry .normal = normal;
         }

         if (this .mustRotateAxes)
            this .rotateAxes90 (this .points);

         coordinate .point = this .points;
         geometry .coord   = coordinate;

         appearance .material = material;
         shape .appearance    = appearance;
         shape .geometry      = geometry;

         scene .getRootNodes () .push (shape);
      }
   },
   createColor ()
   {
      if (!this .colors ?.length)
         return null;

      const
         alpha = this .alpha && this .colors .some ((v, i) => i % 4 === 3 && v < 1),
         color = this .getScene () .createNode (alpha ? "ColorRGBA" : "Color");

      color .color = alpha || !this .alpha ? this .colors : this .colors .filter ((v, i) => i % 4 !== 3);

      return color;
   },
   processElement (element)
   {
      switch (element .name)
      {
         case "vertex":
            this .parseVertices (element);
            break;
         case "face":
            this .parseFaces (element);
            break;
         case "multi_texture_vertex":
            this .parseMultiTextureVertices (element);
            break;
         case "multi_texture_face":
            this .parseMultiTextureFaces (element);
            break;
         default:
            this .parseUnknown (element);
            break;
      }
   },
   parseVertices ({ count, properties })
   {
      // console .time ("vertices");

      // Geometry

      const
         colors    = [ ],
         texCoords = [ ],
         normals   = [ ],
         points    = [ ];

      // Gaussian Splats

      const
         scales      = [ ],
         quaternions = [ ],
         opacities   = [ ],
         sh0         = [ ], // Degree 0
         rest        = Array .from ({ length: 45 }, () => [ ]), // Degree 1,2,3
         restIndex   = new Map (Array .from ({ length: 45 }, (v, i) => [`f_rest_${i}`, i]));

      for (let i = 0; i < count; ++ i)
      {
         this .whitespaces ();

         for (const { value, name, type } of properties)
         {
            if (!value .call (this))
               throw new Error (`Couldn't parse value for property ${name}.`);

            switch (name)
            {
               case "red": case "green": case "blue": case "alpha":
               case "r": case "g": case "b": case "a":
                  colors .push (this .convertColor (this .value, type));
                  break;
               case "s": case "t": case "u": case "v":
                  texCoords .push (this .value);
                  break;
               case "nx": case "ny": case "nz":
                  normals .push (this .value);
                  break;
               case "x": case "y": case "z":
                  points .push (this .value);
                  break;
               // Gaussian Splats
               // https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/ply/
               // https://github.com/mkkellogg/GaussianSplats3D/
               case "rot_0": case "rot_1": case "rot_2": case "rot_3":
                  quaternions .push (this .value);
                  break;
               // https://github.com/javagl/JSplat/blob/41706e0a54372a8ae2e4b474d3a39e19337e42c2/jsplat-io-gsplat/src/main/java/de/javagl/jsplat/io/gsplat/GsplatSplatWriter.java#L106
               case "scale_0": case "scale_1": case "scale_2":
                  scales .push (Math .exp (this .value));
                  break;
               case "opacity":
                  // https://github.com/javagl/JSplat/blob/41706e0a54372a8ae2e4b474d3a39e19337e42c2/jsplat/src/main/java/de/javagl/jsplat/Splats.java#L244
                  opacities .push (1 / (1 + Math .exp (-this .value)));
                  break;
               // Degree 0
               case "f_dc_0": case "f_dc_1": case "f_dc_2":
                  sh0 .push (this .value);
                  break;
               // Degree 1,2,3
               case "f_rest_0":  case "f_rest_1":  case "f_rest_2":
               case "f_rest_3":  case "f_rest_4":  case "f_rest_5":
               case "f_rest_6":  case "f_rest_7":  case "f_rest_8":
               case "f_rest_9":  case "f_rest_10": case "f_rest_11":
               case "f_rest_12": case "f_rest_13": case "f_rest_14":
               case "f_rest_15": case "f_rest_16": case "f_rest_17":
               case "f_rest_18": case "f_rest_19": case "f_rest_20":
               case "f_rest_21": case "f_rest_22": case "f_rest_23":
               case "f_rest_24": case "f_rest_25": case "f_rest_26":
               case "f_rest_27": case "f_rest_28": case "f_rest_29":
               case "f_rest_30": case "f_rest_31": case "f_rest_32":
               case "f_rest_33": case "f_rest_34": case "f_rest_35":
               case "f_rest_36": case "f_rest_37": case "f_rest_38":
               case "f_rest_39": case "f_rest_40": case "f_rest_41":
               case "f_rest_42": case "f_rest_43": case "f_rest_44":
                  rest [restIndex .get (name)] .push (this .value);
                  break;
            }
         }
      }

      // console .timeEnd ("vertices");

      // Geometric properties

      this .alpha     = properties .some (p => p .name .match (/^(?:alpha|a)$/));
      this .colors    = colors;
      this .texCoords = texCoords;
      this .normals   = normals;
      this .points    = points;

      if (sh0 .length)
      {
         // Gaussian Splats
         // https://github.com/javagl/JSplat/blob/41706e0a54372a8ae2e4b474d3a39e19337e42c2/jsplat-io-ply/src/main/java/de/javagl/jsplat/io/ply/PlySplatReader.java#L121

         const
            numSplats     = points .length / 3,
            shDegree      = this .getSphericalHarmonicsDegree (rest),
            shDimensions1 = this .getDimensionsForDegree (shDegree) - 1,
            shDimensions2 = shDimensions1 * 2,
            shs           = Array .from ({ length: 15 }, () => [ ]);

         for (let d = 0; d < shDimensions1; ++ d)
         {
            const
               rx = rest [d],
               ry = rest [shDimensions1 + d],
               rz = rest [shDimensions2 + d],
               sh = shs [d];

            for (let s = 0; s < numSplats; ++ s)
               sh .push (rx [s], ry [s], rz [s]);
         }

         shs .unshift (sh0);

         this .quaternions        = quaternions;
         this .scales             = scales;
         this .opacities          = opacities;
         this .sphericalHarmonics = shs;
      }
   },
   getSphericalHarmonicsDegree (rest)
   {
      if (rest [44] .length)
         return 3;

      if (rest [23] .length)
         return 2;

      if (rest [8] .length)
         return 1;

      return 0;
   },
   getDimensionsForDegree (shDegree)
   {
      return (shDegree + 1) ** 2;
   },
   parseFaces ({ count, properties })
   {
      const coordIndex = [ ];

      for (let i = 0; i < count; ++ i)
      {
         this .whitespaces ();

         for (const { count, value, name } of properties)
         {
            if (!count)
            {
               if (!value .call (this))
                  throw new Error (`Couldn't parse a property value for ${name}.`);

               continue;
            }

            if (!count .call (this))
               throw new Error (`Couldn't parse property count for ${name}.`);

            const length = this .value;

            this .triangles &&= length === 3;

            for (let i = 0; i < length; ++ i)
            {
               if (!value .call (this))
                  throw new Error (`Couldn't parse a property value for ${name}.`);

               coordIndex .push (this .value);
            }

            coordIndex .push (-1);
         }
      }

      this .coordIndex = coordIndex;
   },
   parseMultiTextureVertices ({ count, properties })
   {
      const texCoords = [ ];

      for (let i = 0; i < count; ++ i)
      {
         this .whitespaces ();

         for (const { value, name } of properties)
         {
            if (!value .call (this))
               throw new Error (`Couldn't parse value for property ${name}.`);

            switch (name)
            {
               case "s": case "t":
               case "u": case "v":
                  texCoords .push (this .value);
                  break;
            }
         }
      }

      this .texCoords = texCoords;
   },
   parseMultiTextureFaces ({ count, properties })
   {
      const texCoordIndex = [ ];

      for (let i = 0; i < count; ++ i)
      {
         for (const { count, value, name } of properties)
         {
            if (count)
            {
               if (!count .call (this))
                  throw new Error (`Couldn't parse property count for ${name}.`);

               const length = this .value;

               for (let i = 0; i < length; ++ i)
               {
                  if (!value .call (this))
                     throw new Error (`Couldn't parse value for property ${name}.`);

                  texCoordIndex .push (this .value);
               }

               texCoordIndex .push (-1);
            }
            else
            {
               if (!value .call (this))
                  throw new Error (`Couldn't parse value for property ${name}.`);
            }
         }
      }

      this .texCoordIndex = texCoordIndex;
   },
   parseUnknown ({ count })
   {
      this .whitespaces ();

      for (let i = 0; i < count; ++ i)
         PLYAParser_Grammar .line .parse (this);
   },
   double ()
   {
      this .whitespacesNoLineTerminator ();

      if (PLYAParser_Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);

         return true;
      }

      return false;
   },
   int32 ()
   {
      this .whitespacesNoLineTerminator ();

      if (PLYAParser_Grammar .int32 .parse (this))
      {
         this .value = parseInt (this .result [0]);

         return true;
      }

      return false;
   },
   convertColor (value, type)
   {
      switch (type)
      {
         case "uchar":
         case "uint8":
            return value / 0xff;
         case "ushort":
         case "uint16":
            return value / 0xfffff;
         case "uint":
         case "uint32":
            return value / 0xffffffff;
         case "float":
         case "float32":
         case "double":
         case "float64":
            return value;
      }
   },
});

const PLYAParser_default_ = PLYAParser;
;

/* harmony default export */ const Parser_PLYAParser = (Namespace/* default */.A .add ("PLYAParser", PLYAParser_default_));
;// ./src/x_ite/Parser/PLYBParser.js
/* provided dependency */ var PLYBParser_$ = __webpack_require__(44).A;


function PLYBParser (scene)
{
   Parser_PLYAParser .call (this, scene);

   this .typeMapping = new Map ([
      ["char",    this .binaryInt8],
      ["uchar",   this .binaryUint8],
      ["short",   this .binaryInt16],
      ["ushort",  this .binaryUint16],
      ["int",     this .binaryInt32],
      ["uint",    this .binaryUint32],
      ["float",   this .binaryFloat32],
      ["double",  this .binaryFloat64],
      ["int8",    this .binaryInt8],
      ["uint8",   this .binaryUint8],
      ["int16",   this .binaryInt16],
      ["uint16",  this .binaryUint16],
      ["int32",   this .binaryInt32],
      ["uint32",  this .binaryUint32],
      ["float32", this .binaryFloat32],
      ["float64", this .binaryFloat64],
   ]);

   this .binaryInt8    .bytes = 1;
   this .binaryUint8   .bytes = 1;
   this .binaryInt16   .bytes = 2;
   this .binaryUint16  .bytes = 2;
   this .binaryInt32   .bytes = 4;
   this .binaryUint32  .bytes = 4;
   this .binaryFloat32 .bytes = 4;
   this .binaryFloat64 .bytes = 8;
}

Object .assign (Object .setPrototypeOf (PLYBParser .prototype, Parser_PLYAParser .prototype),
{
   getEncoding ()
   {
      return "ARRAY_BUFFER";
   },
   setInput (input)
   {
      this .arrayBuffer  = input;
      this .dataView     = new DataView (this .arrayBuffer);
      this .input        = PLYBParser_$.decodeText (input .slice (0, 4096));
      this .magic        = this .input .match (/^ply\r?\nformat (binary_(?:little|big)_endian) 1.0.*?end_header\r?\n/s);
      this .byteOffset   = this .magic ?.[0] .length;
      this .littleEndian = this .magic ?.[1] === "binary_little_endian";
   },
   isValid ()
   {
      if (!(this .arrayBuffer instanceof ArrayBuffer))
         return false;

      return !! this .magic;
   },
   binaryInt8 ()
   {
      this .value       = this .dataView .getInt8 (this .byteOffset, this .littleEndian);
      this .byteOffset += 1;

      return true;
   },
   binaryUint8 ()
   {
      this .value       = this .dataView .getUint8 (this .byteOffset, this .littleEndian);
      this .byteOffset += 1;

      return true;
   },
   binaryInt16 ()
   {
      this .value       = this .dataView .getInt16 (this .byteOffset, this .littleEndian);
      this .byteOffset += 2;

      return true;
   },
   binaryUint16 ()
   {
      this .value       = this .dataView .getUint16 (this .byteOffset, this .littleEndian);
      this .byteOffset += 2;

      return true;
   },
   binaryInt32 ()
   {
      this .value       = this .dataView .getInt32 (this .byteOffset, this .littleEndian);
      this .byteOffset += 4;

      return true;
   },
   binaryUint32 ()
   {
      this .value       = this .dataView .getUint32 (this .byteOffset, this .littleEndian);
      this .byteOffset += 4;

      return true;
   },
   binaryFloat32 ()
   {
      this .value       = this .dataView .getFloat32 (this .byteOffset, this .littleEndian);
      this .byteOffset += 4;

      return true;
   },
   binaryFloat64 ()
   {
      this .value       = this .dataView .getFloat64 (this .byteOffset, this .littleEndian);
      this .byteOffset += 8;

      return true;
   },
   processElements (elements)
   {
      this .whitespaces = Function .prototype;

      return Parser_PLYAParser .prototype .processElements .call (this, elements);
   },
   parseUnknown ({ count, properties })
   {
      for (let i = 0; i < count; ++ i)
      {
         for (const { count, type } of properties)
         {
            if (count)
            {
               count .call (this);

               this .byteOffset += this .value * this .typeMapping .get (type) .bytes;
            }
            else
            {
               this .byteOffset += this .typeMapping .get (type) .bytes;
            }
         }
      }
   },
   // parseUnknown ({ count, properties })
   // {
   //    let string = "";

   //    for (let i = 0; i < count; ++ i)
   //    {
   //       for (const { count, type, value } of properties)
   //       {
   //          if (count === undefined)
   //          {
   //             value .call (this);

   //             string += this .value + " "
   //          }
   //          else
   //          {
   //             count .call (this);

   //             string += this .value + " "

   //             for (let i = 0, l = this .value; i < l; ++ i)
   //             {
   //                value .call (this);

   //                string += this .value + " "
   //             }
   //          }
   //       }

   //       string += "\n"
   //    }

   //    console .log (string .substring (0, 1000))
   // },
});

const PLYBParser_default_ = PLYBParser;
;

/* harmony default export */ const Parser_PLYBParser = (Namespace/* default */.A .add ("PLYBParser", PLYBParser_default_));
;// ./src/standard/Math/Geometry/Box2.js



function Box2 (size, center)
{
   this .matrix = new Numbers_Matrix3 ();

   this .set (size, center);
}

Object .assign (Box2 .prototype,
{
   copy ()
   {
      const copy = Object .create (Box2 .prototype);

      copy .matrix = this .matrix .copy ();

      return copy;
   },
   assign (box)
   {
      this .matrix .assign (box .matrix);

      return this;
   },
   equals (box)
   {
      return this .matrix .equals (box .matrix);
   },
   set (size, center)
   {
      if (size && center)
      {
         this .matrix .set (size .x / 2, 0, 0,
                            0, size .y / 2, 0,
                            center .x, center .y, 1);
      }
      else
      {
         this .matrix .assign (Numbers_Matrix3 .ZERO);
      }

      return this;
   },
   setExtents (min, max)
   {
      const
         sx = (max .x - min .x) / 2,
         sy = (max .y - min .y) / 2,
         cx = (max .x + min .x) / 2,
         cy = (max .y + min .y) / 2;

      this .matrix .set (sx,  0, 0,
                          0, sy, 0,
                         cx, cy, 1);

      return this;
   },
   isEmpty ()
   {
      return this .matrix [8] === 0;
   },
   add: (() =>
   {
      const
         lhs_min = new Numbers_Vector2 (),
         lhs_max = new Numbers_Vector2 (),
         rhs_min = new Numbers_Vector2 (),
         rhs_max = new Numbers_Vector2 ();

      return function (box)
      {
         if (this .isEmpty ())
            return this .assign (box);

         if (box .isEmpty ())
            return this;

         this .getExtents (lhs_min, lhs_max);
         box  .getExtents (rhs_min, rhs_max);

         return this .setExtents (lhs_min .min (rhs_min), lhs_max .max (rhs_max));
      };
   })(),
   multLeft (matrix)
   {
      this .matrix .multLeft (matrix);
      return this;
   },
   multRight (matrix)
   {
      this .matrix .multRight (matrix);
      return this;
   },
   getExtents (min, max)
   {
      this .getAbsoluteExtents (min, max);

      min .add (this .center);
      max .add (this .center);
   },
   getAbsoluteExtents: (() =>
   {
      const p1 = new Numbers_Vector2 ();

      return function (min, max)
      {
         const
            m = this .matrix,
            x = m .xAxis,
            y = m .yAxis;

         p1 .assign (x) .add (y);

         const p2 = y .subtract (x);

         min .assign (p1) .min (p2);
         max .assign (p1) .max (p2);

         p1 .negate ();
         p2 .negate ();

         min .min (p1, p2);
         max .max (p1, p2);
      };
   })(),
   containsPoint: (() =>
   {
      const
         min = new Numbers_Vector2 (),
         max = new Numbers_Vector2 ();

      return function (point)
      {
         this .getExtents (min, max);

         return min .x <= point .x &&
                max .x >= point .x &&
                min .y <= point .y &&
                max .y >= point .y;
      };
   })(),
   toString ()
   {
      return `${this .size}, ${this .center}`;
   },
});

Object .assign (Box2,
{
   fromExtents (min, max)
   {
      return new Box2 () .setExtents (min, max);
   },
   fromPoints (points)
   {
      const
         min = new Numbers_Vector2 (Number .POSITIVE_INFINITY),
         max = new Numbers_Vector2 (Number .NEGATIVE_INFINITY);

      for (const point of points)
      {
         min .min (point);
         max .max (point);
      }

      return new Box2 () .setExtents (min, max);
   },
});

Object .defineProperties (Box2 .prototype,
{
   size:
   {
      get: (() =>
      {
         const
            min = new Numbers_Vector2 (),
            max = new Numbers_Vector2 ();

         return function ()
         {
            this .getAbsoluteExtents (min, max);

            return max .subtract (min);
         };
      })(),
      enumerable: true,
   },
   center:
   {
      get ()
      {
         return this .matrix .origin;
      },
      enumerable: true,
   },
});

const Box2_default_ = Box2;
;

/* harmony default export */ const Geometry_Box2 = (Namespace/* default */.A .add ("Box2", Box2_default_));
;// ./src/standard/Math/Algorithms/Bezier.js







const { interval: Bezier_interval } = Math_Algorithm;

// https://pomax.github.io/bezierinfo/

const Bezier =
{
   quadric: (() =>
   {
      const
         c = new Numbers_Matrix3 (1, 0, 0, -2, 2, 0, 1, -2, 1),
         p = new Numbers_Matrix3 ();

      return function (x0, y0, z0, x1, y1, z1, x2, y2, z2, steps, points)
      {
         p
            .set (x0, y0, z0, x1, y1, z1, x2, y2, z2)
            .multLeft (c);

         for (let i = 0, d = steps - 1; i < steps; ++ i)
         {
            const t = i / d;

            points .push (p .multVecMatrix (new Numbers_Vector3 (1, t, t * t)));
         }
      };
   })(),
   cubic: (() =>
   {
      const
         v = new Numbers_Vector4 (),
         c = new Numbers_Matrix4 (1, 0, 0, 0, -3, 3, 0, 0, 3, -6, 3, 0, -1, 3, -3, 1),
         p = new Numbers_Matrix4 ();

      return function (x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3, steps, points)
      {
         p
            .set (x0, y0, z0, 0, x1, y1, z1, 0, x2, y2, z2, 0, x3, y3, z3, 0)
            .multLeft (c);

         for (let i = 0, d = steps - 1; i < steps; ++ i)
         {
            const t = i / d;

            p .multVecMatrix (v .set (1, t, t * t, t * t * t));

            points .push (new Numbers_Vector3 (v .x, v .y, v .z));
         }
      };
   })(),
   arc (ax, ay, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y, steps, points)
   {
      // https://ericeastwood.com/blog/25/curves-and-arcs-quadratic-cubic-elliptical-svg-implementations
      // See https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes.

      // If the endpoints are identical, then this is equivalent to omitting the elliptical arc segment entirely.
      if (ax === x && ay === y)
      {
         points .push (new Numbers_Vector3 (x, y, 0));
         return;
      }

      // In accordance to: http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters

      rx = Math .abs (rx);
      ry = Math .abs (ry);

      // If rx = 0 or ry = 0 then this arc is treated as a straight line segment joining the endpoints.
      if (rx === 0 || ry === 0)
      {
         points .push (new Numbers_Vector3 (ax, ay, 0), new Numbers_Vector3 (x, y, 0));
         return;
      }

      const
         rx2 = rx * rx,
         ry2 = ry * ry;

      // In accordance to: http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters

      xAxisRotation = Bezier_interval (xAxisRotation, 0, 2 * Math .PI);

      const
         sinRotation = Math .sin (xAxisRotation),
         cosRotation = Math .cos (xAxisRotation);

      // Following "Conversion from endpoint to center parameterization"
      // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter

      // Step #1: Compute transformedPoint
      const d = new Numbers_Vector2 (ax - x, ay - y) .divide (2);

      const transformedPoint = new Numbers_Vector2 ( cosRotation * d .x + sinRotation * d .y,
                                             -sinRotation * d .x + cosRotation * d .y);

      const transformedPoint2 = transformedPoint .copy () .multVec (transformedPoint);

      // Ensure radii are large enough
      const radiiCheck = transformedPoint2 .x / rx2 + transformedPoint2 .y / ry2;

      if (radiiCheck > 1)
      {
         rx = Math .sqrt (radiiCheck) * rx;
         ry = Math .sqrt (radiiCheck) * ry;
      }

      // Step #2: Compute transformedCenter
      const cSquareNumerator = rx2 * ry2 - rx2 * transformedPoint2 .y - ry2 * transformedPoint2 .x;
      const cSquareRootDenom =             rx2 * transformedPoint2 .y + ry2 * transformedPoint2 .x;
      let   cRadicand        = cSquareNumerator / cSquareRootDenom;

      // Make sure this never drops below zero because of precision
      cRadicand = Math .max (0, cRadicand);

      const cCoef = (largeArcFlag !== sweepFlag ? 1 : -1) * Math .sqrt (cRadicand);

      const transformedCenter = new Numbers_Vector2 ( cCoef * rx * transformedPoint .y / ry,
                                             -cCoef * ry * transformedPoint .x / rx);

      // Step #3: Compute center
      const center = new Numbers_Vector2 (cosRotation * transformedCenter .x - sinRotation * transformedCenter .y + ((ax + x) / 2),
                                  sinRotation * transformedCenter .x + cosRotation * transformedCenter .y + ((ay + y) / 2));

      // Step #4: Compute start/sweep angles
      const startVector = new Numbers_Vector2 ((transformedPoint .x - transformedCenter .x) / rx,
                                       (transformedPoint .y - transformedCenter .y) / ry);

      const endVector = new Numbers_Vector2 ((-transformedPoint .x - transformedCenter .x) / rx,
                                     (-transformedPoint .y - transformedCenter .y) / ry);

      const get_angle  = (x) => { return x > 0 ? x : 2 * Math .PI + x; }; // transform angle to range [0, 2pi]
      const startAngle = get_angle (Math .atan2 (startVector .y, startVector .x));
      const endAngle   = get_angle (Math .atan2 (endVector   .y, endVector   .x));

      let sweepAngle = endAngle - startAngle;

      if (largeArcFlag)
      {
         // sweepAngle must be positive
         if (sweepAngle < 0)
            sweepAngle += 2 * Math .PI;
      }
      else
      {
         // sweepAngle must be negative
         if (sweepAngle > 0)
            sweepAngle -= 2 * Math .PI;
      }

      if (sweepFlag && sweepAngle < 0)
         sweepAngle += 2 *Math .PI;

      else if (!sweepFlag && sweepAngle > 0)
         sweepAngle -= 2 * Math .PI;

      // Interpolate:

      const bezier_steps   = Math .max (4, Math .abs (sweepAngle) * steps / (2 * Math .PI));
      const bezier_steps_1 = bezier_steps - 1;

      points .push (new Numbers_Vector3 (ax, ay, 0));

      for (let i = 1; i < bezier_steps_1; ++ i)
      {
         const t = i / bezier_steps_1;

         // From http://www.w3.org/TR/SVG/implnote.html#ArcParameterizationAlternatives
         const angle = startAngle + (sweepAngle * t);
         const x     = rx * Math .cos (angle);
         const y     = ry * Math .sin (angle);

         points .push (new Numbers_Vector3 (cosRotation * x - sinRotation * y + center .x,
                                    sinRotation * x + cosRotation * y + center .y,
                                    0));
      }

      points .push (new Numbers_Vector3 (x, y, 0));
   },
};

const Bezier_default_ = Bezier;
;

/* harmony default export */ const Algorithms_Bezier = (Namespace/* default */.A .add ("Bezier", Bezier_default_));
;// ./src/x_ite/Parser/SVGParser.js
/* provided dependency */ var SVGParser_$ = __webpack_require__(44).A;
/* provided dependency */ var SVGParser_libtess = __webpack_require__(872);

















/*
 *  Grammar
 */

// Lexical elements
const SVGParser_Grammar = Parser_Expressions ({
   // General
   whitespaces: /[\x20\n\t\r]+/y,
   comma: /,/y,
   openParenthesis: /\(/y,
   closeParenthesis: /\)/y,

   // Units
   length: /em|ex|px|in|cm|mm|pt|pc|%/y,
   percent: /%/y,

   // Values
   int32: /(?:0[xX][\da-fA-F]+)|(?:[+-]?\d+)/y,
   double: /[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+(?:\.)?))(?:[eE][+-]?\d+)?)/y,
   constants: /([+-])((?:NAN|INF|INFINITY))/iy,
   matrix: /matrix/y,
   translate: /translate/y,
   rotate: /rotate/y,
   scale: /scale/y,
   skewX: /skewX/y,
   skewY: /skewY/y,
   color: /#[\da-fA-F]+|[a-zA-Z]+\([^\)]*\)|[a-zA-Z]+/y,
   url: /url\("?(.*?)"?\)/y,
   path: /[mMlLhHvVqQtTcCsSaAzZ]/y,
});

/*
 *  Constants
 */

const
   MM     = 0.001,     // One mm in meters.
   CM     = 0.01,      // One cm in meters.
   INCH   = 0.0254,    // One inch in meters.
   POINT  = INCH / 72, // One point in meters.
   PICA   = INCH / 6,  // One pica in meters.
   PIXEL  = INCH / 90, // One pixel in meters.
   EM     = 16,        // One em in pixels.
   SPREAD = 16;        // Spread factor, Integer.

/*
 *  Parser
 */

function SVGParser (scene)
{
   Parser_X3DParser    .call (this, scene);
   Parser_X3DOptimizer .call (this);

   // Optimizer

   this .removeEmptyGroups    = true;
   this .combineGroupingNodes = true;

   // Options

   this .solid = false; // Are 2D primitives solid?

   // Globals

   this .viewBox          = new Numbers_Vector4 (0, 0, 100, 100);
   this .modelMatrix      = new Utility_MatrixStack (Numbers_Matrix4);
   this .fillGeometries   = new Map ();
   this .strokeGeometries = new Map ();
   this .lineProperties   = new Map ();
   this .tessy            = this .createTesselator ();
   this .context          = document .createElement ("canvas") .getContext ("2d", { willReadFrequently: true });
   this .numSwitchNodes   = 0;

   this .styles = [{
      display: "inline",
      fillType: "COLOR",
      fillColor: Numbers_Color4 .BLACK,
      fillURL: "",
      fillOpacity: 1,
      fillRule: "nonzero",
      strokeType: "none",
      strokeColor: Numbers_Color4 .BLACK,
      strokeURL: "",
      strokeOpacity: 1,
      strokeWidth: 1,
      opacity: 1,
      stopColor: Numbers_Color4 .BLACK,
      stopOpacity: 1,
      vectorEffect: "none",
   }];

   // Constants

   const browser = scene .getBrowser ()

   switch (browser .getBrowserOption ("PrimitiveQuality"))
   {
      case "LOW":
         this .BEZIER_STEPS = 9;  // Subdivisions of a span.
         this .CIRCLE_STEPS = 20; // Subdivisions of a circle, used for arc and rounded rect.
         break;
      case "HIGH":
         this .BEZIER_STEPS = 15; // Subdivisions of a span.
         this .CIRCLE_STEPS = 64; // Subdivisions of a circle, used for arc and rounded rect.
         break;
      default:
         this .BEZIER_STEPS = 12;  // Subdivisions of a span.
         this .CIRCLE_STEPS = 32; // Subdivisions of a circle, used for arc and rounded rect.
         break;
   }

   switch (browser .getBrowserOption ("TextureQuality"))
   {
      case "LOW":
         this .GRADIENT_SIZE = 128; // In pixels.
         break;
      case "HIGH":
         this .GRADIENT_SIZE = 512; // In pixels.
         break;
      default:
         this .GRADIENT_SIZE = 256; // In pixels.
         break;
   }

   // Use .canvas to support foreign 2d libs.
   this .context .canvas .width  = this .GRADIENT_SIZE;
   this .context .canvas .height = this .GRADIENT_SIZE;
}

Object .assign (Object .setPrototypeOf (SVGParser .prototype, Parser_X3DParser .prototype),
   Parser_X3DOptimizer .prototype,
{
   CONSTANTS: new Map ([
      ["NAN", NaN],
      ["INF", Infinity],
      ["INFINITY", Infinity],
   ]),
   getEncoding ()
   {
      return "XML";
   },
   setInput (xmlElement)
   {
      try
      {
         if (typeof xmlElement === "string")
            xmlElement = SVGParser_$.parseXML (xmlElement);

         this .input = xmlElement;
      }
      catch
      {
         this .input = undefined;
      }
   },
   isValid ()
   {
      if (!(this .input instanceof XMLDocument))
         return false;

      if (SVGParser_$(this .input) .children ("svg") .length)
         return true;

      if (this .input .nodeName === "svg")
         return true;

      return false;
   },
   parseIntoScene (resolve, reject)
   {
      this .xmlElement (this .input)
         .then (resolve)
         .catch (reject);
   },
   async xmlElement (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "#document":
         {
            const svg = SVGParser_$(xmlElement) .children ("svg");

            for (const xmlElement of svg)
               await this .svgElement (xmlElement);

            break;
         }
         case "svg":
         {
            await this .svgElement (xmlElement);
            break;
         }
      }

      return this .getScene ();
   },
   async svgElement (xmlElement)
   {
      const
         browser = this .getBrowser (),
         scene   = this .getScene ();

      scene .setEncoding ("SVG");
      scene .setProfile (browser .getProfile ("Interchange"));
      scene .updateComponent (browser .getComponent ("Geometry2D", 2));

      await browser .loadComponents (scene);

      // Init nodes.

      this .document              = this .input;
      this .rootTransform         = scene .createNode ("Transform");
      this .groupNodes            = [this .rootTransform];
      this .texturePropertiesNode = this .createTextureProperties ();

      // Create background.

      const background = scene .createNode ("Background");

      background .skyColor = [1, 1, 1];

      scene .getRootNodes () .push (background);

      // Create navigation info.

      const navigationInfo = scene .createNode ("NavigationInfo");

      navigationInfo .type = ["PLANE", "PLANE_create3000.github.io", "EXAMINE", "ANY"];

      // Get attributes of svg element.

      const
         defaultWidth   = this .lengthAttribute (xmlElement .getAttribute ("width"),  300, "width"),
         defaultHeight  = this .lengthAttribute (xmlElement .getAttribute ("height"), 150, "height"),
         defaultViewBox = this .viewBox .set (0, 0, defaultWidth, defaultHeight),
         viewBox        = this .viewBoxAttribute (xmlElement .getAttribute ("viewBox"), defaultViewBox),
         width          = this .lengthAttribute (xmlElement .getAttribute ("width"),  viewBox [2], "width"),
         height         = this .lengthAttribute (xmlElement .getAttribute ("height"), viewBox [3], "height");

      if (true) // default
      {
         // preserveAspectRatio = "xMidYMid meet"

         const
            r  = width / height,
            rv = viewBox [2] / viewBox [3];

         if (rv > r)
            viewBox [3] += viewBox [2] / r - viewBox [3];
         else
            viewBox [2] += viewBox [3] * r - viewBox [2];
      }

      // Create viewpoint.

      const
         viewpoint = scene .createNode ("OrthoViewpoint"),
         x         =  (viewBox .x + width  / 2) * PIXEL,
         y         = -(viewBox .y + height / 2) * PIXEL;

      viewpoint .navigationInfo   = navigationInfo;
      viewpoint .position         = new Numbers_Vector3 (x, y, 10);
      viewpoint .centerOfRotation = new Numbers_Vector3 (x, y, 0);

      viewpoint .fieldOfView = new Numbers_Vector4 (
         -width  / 2 * PIXEL,
         -height / 2 * PIXEL,
          width  / 2 * PIXEL,
          height / 2 * PIXEL,
      );

      scene .getRootNodes () .push (viewpoint);

      // Create view matrix.

      const scale = new Numbers_Vector3 (width * PIXEL / viewBox [2], -height * PIXEL / viewBox [3], 1);

      this .rootTransform .scale = scale;

      // Parse elements.

      this .elements (xmlElement);

      // Add root Transform node.

      scene .addNamedNode (scene .getUniqueName ("ViewBox"), this .rootTransform);
      scene .addExportedNode (scene .getUniqueExportName ("ViewBox"), this .rootTransform);
      scene .getRootNodes () .push (this .rootTransform);

      // Optimize scene graph.

      this .optimizeSceneGraph (scene .getRootNodes ());
   },
   elements (xmlElement)
   {
      for (const childNode of xmlElement .childNodes)
         this .element (childNode);
   },
   element (xmlElement)
   {
      switch (xmlElement .nodeName)
      {
         case "use":
            return this .useElement (xmlElement);
         case "g":
            return this .gElement (xmlElement);
         case "switch":
            return this .switchElement (xmlElement);
         case "a":
            return this .aElement (xmlElement);
         case "rect":
            return this .rectElement (xmlElement);
         case "circle":
            return this .circleElement (xmlElement);
         case "ellipse":
            return this .ellipseElement (xmlElement);
         case "text":
            return this .textElement (xmlElement);
         case "image":
            return this .imageElement (xmlElement);
         case "polyline":
            return this .polylineElement (xmlElement);
         case "polygon":
            return this .polygonElement (xmlElement);
         case "path":
            return this .pathElement (xmlElement);
      }
   },
   useElement (xmlElement)
   {
      // Get href.

      const usedElement = this .hrefAttribute (xmlElement .getAttribute ("href") || xmlElement .getAttribute ("xlink:href"));

      if (!usedElement)
         return;

      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Create Transform node.

      const
         x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0, "width"),
         y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0, "height");

      const transformNode = this .createTransform (xmlElement, new Numbers_Vector2 (x, y));

      this .groupNodes .push (transformNode);

      this .element (usedElement);

      this .popAll ();
   },
   gElement (xmlElement)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Create Transform node.

      const transformNode = this .createTransform (xmlElement);

      // Get child elements.

      this .groupNodes .push (transformNode);

      this .elements (xmlElement);

      this .popAll ();
   },
   switchElement (xmlElement)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Create Transform node.

      const
         scene         = this .getExecutionContext (),
         transformNode = this .createTransform (xmlElement),
         switchNode    = scene .createNode ("Switch");

      transformNode .children .push (switchNode);

      switchNode .whichChoice = 0;

      scene .addExportedNode (scene .getUniqueExportName (`Switch${++ this .numSwitchNodes}`), node);

      // Get child elements.

      this .groupNodes .push (switchNode);

      this .elements (xmlElement);

      this .popAll ();
   },
   aElement (xmlElement)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Get attributes.

      const
         href   = xmlElement .getAttribute ("href") || xmlElement .getAttribute ("xlink:href"),
         title  = xmlElement .getAttribute ("title") || xmlElement .getAttribute ("xlink:title"),
         target = xmlElement .getAttribute ("target");

      // Create Transform node.

      const
         scene         = this .getExecutionContext (),
         transformNode = this .createTransform (xmlElement),
         anchorNode    = scene .createNode ("Anchor");

      transformNode .children .push (anchorNode);

      anchorNode .description = title;
      anchorNode .url         = [href];

      if (target)
         anchorNode .parameter = [`target=${target}`];

      // Get child elements.

      this .groupNodes .push (anchorNode);

      this .elements (xmlElement);

      this .popAll ();
   },
   rectElement (xmlElement)
   {
      // Create Transform node.

      const
         x      = this .lengthAttribute (xmlElement .getAttribute ("x"),      0, "width"),
         y      = this .lengthAttribute (xmlElement .getAttribute ("y"),      0, "height"),
         width  = this .lengthAttribute (xmlElement .getAttribute ("width"),  0, "width"),
         height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0, "height");

      let
         rx = Math .max (0, this .lengthAttribute (xmlElement .getAttribute ("rx"), 0, "width")),
         ry = Math .max (0, this .lengthAttribute (xmlElement .getAttribute ("ry"), 0, "height"));

      if (rx === 0 && ry === 0)
      {
         // Determine style.

         if (!this .styleAttributes (xmlElement))
            return;

         // Create Transform node.

         const
            scene         = this .getExecutionContext (),
            size          = new Numbers_Vector2 (width, height),
            center        = new Numbers_Vector2 (x + width / 2, y + height / 2),
            bbox          = new Geometry_Box2 (size, center),
            transformNode = this .createTransform (xmlElement, center);

         this .groupNodes .push (transformNode);

         // Create nodes.

         if (this .style .fillType !== "none")
         {
            const
               shapeNode     = scene .createNode ("Shape"),
               rectangleNode = this .fillGeometries .get (xmlElement);

            transformNode .children .push (shapeNode);
            shapeNode .appearance = this .createFillAppearance (bbox);

            if (rectangleNode)
            {
               shapeNode .geometry = rectangleNode;
            }
            else
            {
               const rectangleNode = scene .createNode ("Rectangle2D");

               this .fillGeometries .set (xmlElement, rectangleNode);

               shapeNode .geometry  = rectangleNode;
               rectangleNode .solid = this .solid;
               rectangleNode .size  = size;
            }
         }

         if (this .style .strokeType !== "none")
         {
            const
               shapeNode    = scene .createNode ("Shape"),
               polylineNode = this .strokeGeometries .get (xmlElement);

            transformNode .children .push (shapeNode);
            shapeNode .appearance = this .createStrokeAppearance ();

            if (polylineNode)
            {
               shapeNode .geometry = polylineNode;
            }
            else
            {
               const
                  polylineNode = scene .createNode ("Polyline2D"),
                  width1_2     = width / 2,
                  height1_2    = height / 2;

               this .strokeGeometries .set (xmlElement, polylineNode);

               shapeNode .geometry = polylineNode;

               polylineNode .lineSegments = [ width1_2,  height1_2,
                                             -width1_2,  height1_2,
                                             -width1_2, -height1_2,
                                              width1_2, -height1_2,
                                              width1_2,  height1_2];
            }
         }

         this .popAll ();
      }
      else
      {
         // Create points.

         if (rx && !ry) ry = rx;
         if (ry && !rx) rx = ry;

         rx = Math .min (rx, width / 2);
         ry = Math .min (ry, height / 2);

         const
            xOffsets = [x + width - rx, x + rx , x + rx, x + width - rx],
            yOffsets = [y + height - ry, y + height - ry, y + ry, y + ry],
            points   = Object .assign ([ ], { closed: true });

         for (let c = 0; c < 4; ++ c)
         {
            const s = c * Math .PI / 2;

            for (let i = 0, N = this .CIRCLE_STEPS / 4; i < N; ++ i)
            {
               const p = Numbers_Complex .fromPolar (1, s + Math .PI / 2 * i / (N - 1));

               points .push (new Numbers_Vector3 (xOffsets [c] + p .real * rx, yOffsets [c] + p .imag * ry, 0));
            }
         }

         points .pop ();

         // Create nodes.

         this .pathLikeElement (xmlElement, [points]);
      }
   },
   circleElement (xmlElement)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Create Transform node.

      const
         cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0, "width"),
         cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0, "height"),
         r  = this .lengthAttribute (xmlElement .getAttribute ("r"),  0);

      const
         scene         = this .getExecutionContext (),
         bbox          = new Geometry_Box2 (new Numbers_Vector2 (r * 2, r * 2), new Numbers_Vector2 (cx, cy)),
         transformNode = this .createTransform (xmlElement, new Numbers_Vector2 (cx, cy));

      this .groupNodes .push (transformNode);

      // Create nodes.

      if (this .style .fillType !== "none")
      {
         const
            shapeNode = scene .createNode ("Shape"),
            diskNode  = this .fillGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createFillAppearance (bbox);

         if (diskNode)
         {
            shapeNode .geometry = diskNode;
         }
         else
         {
            const diskNode = scene .createNode ("Disk2D");

            this .fillGeometries .set (xmlElement, diskNode);

            shapeNode .geometry   = diskNode;
            diskNode .solid       = this .solid;
            diskNode .outerRadius = r;
         }
      }

      if (this .style .strokeType !== "none")
      {
         const
            shapeNode  = scene .createNode ("Shape"),
            circleNode = this .strokeGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createStrokeAppearance ();

         if (circleNode)
         {
            shapeNode .geometry = circleNode;
         }
         else
         {
            const circleNode = scene .createNode ("Circle2D");

            this .strokeGeometries .set (xmlElement, circleNode);

            shapeNode .geometry = circleNode;
            circleNode .radius  = r;
         }
      }

      this .popAll ();
   },
   ellipseElement (xmlElement)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Create Transform node.

      const
         cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0, "width"),
         cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0, "height"),
         rx = this .lengthAttribute (xmlElement .getAttribute ("rx"), 0, "width"),
         ry = this .lengthAttribute (xmlElement .getAttribute ("ry"), 0, "height");

      const
         scene         = this .getExecutionContext (),
         rMin          = Math .min (rx, ry),
         bbox          = new Geometry_Box2 (new Numbers_Vector2 (rx * 2, ry * 2), new Numbers_Vector2 (cx, cy)),
         transformNode = this .createTransform (xmlElement, new Numbers_Vector2 (cx, cy), new Numbers_Vector2 (rx / rMin, ry / rMin));

      this .groupNodes .push (transformNode);

      // Create nodes.

      if (this .style .fillType !== "none")
      {
         const
            shapeNode = scene .createNode ("Shape"),
            diskNode  = this .fillGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createFillAppearance (bbox);

         if (diskNode)
         {
            shapeNode .geometry = diskNode;
         }
         else
         {
            const diskNode = scene .createNode ("Disk2D");

            this .fillGeometries .set (xmlElement, diskNode);

            shapeNode .geometry   = diskNode;
            diskNode .solid       = this .solid;
            diskNode .outerRadius = rMin;
         }
      }

      if (this .style .strokeType !== "none")
      {
         const
            shapeNode  = scene .createNode ("Shape"),
            circleNode = this .strokeGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createStrokeAppearance ();

         if (circleNode)
         {
            shapeNode .geometry = circleNode;
         }
         else
         {
            const circleNode = scene .createNode ("Circle2D");

            this .strokeGeometries .set (xmlElement, circleNode);

            shapeNode .geometry = circleNode;
            circleNode .radius  = rMin;
         }
      }

      this .popAll ();
   },
   textElement (xmlElement)
   {

   },
   imageElement (xmlElement)
   {
      const transformNode = this .fillGeometries .get (xmlElement);

      if (transformNode)
      {
         this .groupNodes .at (-1) .children .push (transformNode);
      }
      else
      {
         // Determine style.

         if (!this .styleAttributes (xmlElement))
            return;

         // Create Transform node.

         const
            x      = this .lengthAttribute (xmlElement .getAttribute ("x"),      0, "width"),
            y      = this .lengthAttribute (xmlElement .getAttribute ("y"),      0, "height"),
            width  = this .lengthAttribute (xmlElement .getAttribute ("width"),  0, "width"),
            height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0, "height"),
            href   = xmlElement .getAttribute ("href") || xmlElement .getAttribute ("xlink:href");

         const
            scene         = this .getExecutionContext (),
            transformNode = this .createTransform (xmlElement, new Numbers_Vector2 (x + width / 2, y + height / 2), new Numbers_Vector2 (1, -1));

         this .fillGeometries .set (xmlElement, transformNode);
         this .groupNodes .push (transformNode);

         // Create nodes.

         const
            shapeNode      = scene .createNode ("Shape"),
            appearanceNode = scene .createNode ("Appearance"),
            textureNode    = scene .createNode ("ImageTexture"),
            rectangleNode  = scene .createNode ("Rectangle2D");

         shapeNode .appearance          = appearanceNode;
         shapeNode .geometry            = rectangleNode;
         appearanceNode .texture        = textureNode;
         textureNode .url               = [href];
         textureNode .textureProperties = this .texturePropertiesNode;
         rectangleNode .solid           = this .solid;
         rectangleNode .size            = new Numbers_Vector2 (width, height);

         transformNode .children .push (shapeNode);

         this .popAll ();
      }
   },
   polylineElement (xmlElement)
   {
      // Get points.

      const points = [ ];

      if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
         return;

      // Create nodes.

      this .pathLikeElement (xmlElement, [points]);
   },
   polygonElement (xmlElement)
   {
      // Get points.

      const points = Object .assign ([ ], { closed: true });

      if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
         return;

      // Create nodes.

      this .pathLikeElement (xmlElement, [points]);
   },
   pathElement (xmlElement)
   {
      // Get path points.

      const contours = [ ];

      if (!this .dAttribute (xmlElement .getAttribute ("d"), contours))
         return;

      // Create nodes.

      this .pathLikeElement (xmlElement, contours);
   },
   pathLikeElement (xmlElement, contours)
   {
      // Determine style.

      if (!this .styleAttributes (xmlElement))
         return;

      // Filter consecutive equal points.

      const EPSILON = 1e-9; // Min point distance.

      contours = contours .map (points =>
      {
         if (points .closed)
         {
            return Object .assign (points .filter ((p, i, a) => p .distance (a [(i + 1) % a .length]) > EPSILON),
            {
               closed: true,
            });
         }
         else
         {
            return points .filter ((p, i, a) => !i || p .distance (a [i - 1]) > EPSILON);
         }
      })
      .filter (points => points .length > 2);

      // Add index property to points.

      contours .forEach ((points, i, a) => points .index = i ? a [i - 1] .index + a [i - 1] .length : 0);

      // Create Transform node.

      const
         scene         = this .getExecutionContext (),
         transformNode = this .createTransform (xmlElement),
         bbox          = new Geometry_Box2 ();

      for (const points of contours)
         bbox .add (Geometry_Box2 .fromPoints (points));

      this .groupNodes .push (transformNode);

      // Create nodes.

      const
         coordinateNode = scene .createNode ("Coordinate"),
         vertices       = coordinateNode .point;

      for (const points of contours)
      {
         for (const point of points)
            vertices .push (point);
      }

      if (this .style .fillType !== "none")
      {
         const
            shapeNode    = scene .createNode ("Shape"),
            geometryNode = this .fillGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createFillAppearance (bbox);

         if (geometryNode)
         {
            shapeNode .geometry = geometryNode;
         }
         else
         {
            const geometryNode = scene .createNode ("IndexedTriangleSet");

            this .fillGeometries .set (xmlElement, geometryNode);

            shapeNode .geometry    = geometryNode;
            geometryNode .solid    = this .solid;
            geometryNode .index    = this .triangulatePolygon (contours, coordinateNode);
            geometryNode .texCoord = this .createTextureCoordinate (coordinateNode, bbox, shapeNode .appearance);
            geometryNode .coord    = coordinateNode;
         }
      }

      if (this .style .strokeType !== "none")
      {
         const
            shapeNode    = scene .createNode ("Shape"),
            geometryNode = this .strokeGeometries .get (xmlElement);

         transformNode .children .push (shapeNode);
         shapeNode .appearance = this .createStrokeAppearance ();

         if (geometryNode)
         {
            shapeNode .geometry = geometryNode;
         }
         else
         {
            const geometryNode = scene .createNode ("IndexedLineSet");

            this .strokeGeometries .set (xmlElement, geometryNode);

            shapeNode .geometry = geometryNode;
            geometryNode .coord = coordinateNode;

            // Create contour indices.

            const indices = [ ];

            for (const points of contours)
            {
               for (const i of points .keys ())
                  indices .push (points .index + i);

               if (points .closed)
                  indices .push (points .index);

               indices .push (-1);
            }

            geometryNode .coordIndex = indices;
         }
      }

      this .popAll ();
   },
   linearGradientElementUrl (xmlElement, bbox)
   {
      const
         g        = this .linearGradientElement (xmlElement, bbox, { stops: [ ] }),
         gradient = this .context .createLinearGradient (g .x1, g .y1, g .x2, g .y2);

      return this .drawGradient (gradient, g, bbox);
   },
   linearGradientElement (xmlElement, bbox, gradient)
   {
      if (xmlElement .nodeName !== "linearGradient")
         return;

      // Attribute xlink:href

      const refElement = this .hrefAttribute (xmlElement .getAttribute ("href") || xmlElement .getAttribute ("xlink:href"));

      if (refElement)
         this .gradientElement (refElement, bbox, gradient);

      // Attributes

      gradient .x1        = this .lengthAttribute (xmlElement .getAttribute ("x1"), gradient .x1 || 0, "width");
      gradient .y1        = this .lengthAttribute (xmlElement .getAttribute ("y1"), gradient .y1 || 0, "height");
      gradient .x2        = this .lengthAttribute (xmlElement .getAttribute ("x2"), gradient .x2 || 1, "width");
      gradient .y2        = this .lengthAttribute (xmlElement .getAttribute ("y2"), gradient .y2 || 0, "height");
      gradient .units     = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
      gradient .transform = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));

      // Spread matrix

      const
         s = new Numbers_Matrix3 (),
         c = new Numbers_Vector2 (gradient .x1, gradient .y1);

      s .translate (c);
      s .scale (new Numbers_Vector2 (SPREAD, SPREAD));
      s .translate (c .negate ());

      gradient .spreadMatrix = s;

      // Stops

      for (const childNode of xmlElement .childNodes)
         this .gradientChild (childNode, gradient);

      return gradient;
   },
   radialGradientElementUrl (xmlElement, bbox)
   {
      const
         g        = this .radialGradientElement (xmlElement, bbox, { stops: [ ] }),
         gradient = this .context .createRadialGradient (g .fx, g .fy, g .fr, g .cx, g .cy, g .r);

      return this .drawGradient (gradient, g, bbox);
   },
   radialGradientElement (xmlElement, bbox, gradient)
   {
      // Attribute xlink:href

      const refElement = this .hrefAttribute (xmlElement .getAttribute ("href") || xmlElement .getAttribute ("xlink:href"));

      if (refElement)
         this .gradientElement (refElement, bbox, gradient);

      // Attributes

      gradient .cx           = this .lengthAttribute (xmlElement .getAttribute ("cx"), gradient .cx || 0.5, "width"),
      gradient .cy           = this .lengthAttribute (xmlElement .getAttribute ("cy"), gradient .cy || 0.5, "height"),
      gradient .r            = this .lengthAttribute (xmlElement .getAttribute ("r"),  gradient .r  || 0.5),
      gradient .fx           = this .lengthAttribute (xmlElement .getAttribute ("fx"), gradient .fx || gradient .cx, "width"),
      gradient .fy           = this .lengthAttribute (xmlElement .getAttribute ("fy"), gradient .fy || gradient .cy, "height"),
      gradient .fr           = this .lengthAttribute (xmlElement .getAttribute ("fr"), gradient .fr || 0),
      gradient .units        = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
      gradient .spreadMethod = xmlElement .getAttribute ("spreadMethod");
      gradient .transform    = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));

      // Spread matrix

      const
         s = new Numbers_Matrix3 (),
         c = new Numbers_Vector2 (gradient .fx, gradient .fy);

      s .translate (c);
      s .scale (new Numbers_Vector2 (SPREAD));
      s .translate (c .negate ());

      gradient .spreadMatrix = s;

      // Stops

      for (const childNode of xmlElement .childNodes)
         this .gradientChild (childNode, gradient);

      return gradient;
   },
   gradientElement (xmlElement, bbox, gradient)
   {
      if (!xmlElement)
         return;

      switch (xmlElement .nodeName)
      {
         case "linearGradient":
            return this .linearGradientElement (xmlElement, bbox, gradient);
         case "radialGradient":
            return this .radialGradientElement (xmlElement, bbox, gradient);
      }
   },
   gradientChild (xmlElement, gradient)
   {
      if (xmlElement .nodeName === "stop")
         return this .stopElement (xmlElement, gradient);
   },
   stopElement (xmlElement, gradient)
   {
      if (!this .styleAttributes (xmlElement))
         return;

      const offset = this .percentAttribute (xmlElement .getAttribute ("offset"), 0);

      if (offset < 0 || offset > 1)
         return;

      const { stopColor, stopOpacity } = this .style;

      gradient .stops .push ([offset, stopColor, stopOpacity]);

      this .styles .pop ();
   },
   drawGradient (gradient, g, bbox)
   {
      // Add color stops.

      switch (g .spreadMethod)
      {
         default: // pad
         {
            g .spreadMatrix .set ();

            for (const [o, c, a] of g .stops)
               gradient .addColorStop (o, this .cssColor (c, a));

            break;
         }
         case "repeat":
         {
            for (let i = 0; i < SPREAD; ++ i)
            {
               const s = i / SPREAD;

               for (const [o, c, a] of g .stops)
                  gradient .addColorStop (s + o / SPREAD, this .cssColor (c, a));
            }

            break;
         }
         case "reflect":
         {
            for (let i = 0; i < SPREAD; ++ i)
            {
               const s = i / SPREAD;

               for (const [o, c, a] of g .stops)
                  gradient .addColorStop (s + (i % 2 ? 1 - o : o) / SPREAD, this .cssColor (c, a));
            }

            break;
         }
      }

      // Create Matrix.

      const m = new Numbers_Matrix3 ();

      m .scale (new Numbers_Vector2 (this .GRADIENT_SIZE / 2));
      m .translate (Numbers_Vector2 .ONE);
      m .scale (new Numbers_Vector2 (1, -1));

      if (g .units === "userSpaceOnUse")
         m .multLeft (bbox .matrix .copy () .inverse ());
      else
         m .multLeft (new Numbers_Matrix3 (2, 0, 0, 0, 2, 0, -1, -1, 1));

      m .multLeft (g .transform);
      m .multLeft (g .spreadMatrix);

      // Paint.

      const cx = this .context;

      cx .fillStyle = gradient;
      cx .save ();
      cx .clearRect (0, 0, this .GRADIENT_SIZE, this .GRADIENT_SIZE);
      cx .rect (0, 0, this .GRADIENT_SIZE, this .GRADIENT_SIZE);
      cx .transform (m [0], m [1], m [3], m [4], m [6], m [7]);
      cx .fill ();
      cx .restore ();

      // Use PNG because image can have alpha channel.
      return this .context .canvas .toDataURL ("image/png");
   },
   patternUrl (xmlElement)
   {
      //console .debug ("pattern");
   },
   idAttribute (attribute, node)
   {
      if (attribute === null)
         return;

      const
         scene = this .getExecutionContext (),
         name  = this .sanitizeName (attribute);

      if (!name)
         return;

      scene .addNamedNode (scene .getUniqueName (name), node);
      scene .addExportedNode (scene .getUniqueExportName (name), node);
   },
   viewBoxAttribute (attribute, defaultValue)
   {
      if (attribute === null)
         return defaultValue;

      this .parseValue (attribute);

      if (this .double ())
      {
         const x = this .value;

         if (this .double ())
         {
            const y = this .value;

            if (this .double ())
            {
               const width = this .value;

               if (this .double ())
               {
                  const height = this .value;

                  return new Numbers_Vector4 (x, y, width, height);
               }
            }
         }
      }

      return defaultValue;
   },
   hrefAttribute (attribute)
   {
      if (!attribute)
         return;

      const
         scene = this .getExecutionContext (),
         hash  = new URL (attribute, scene .getBaseURL ()) .hash .slice (1);

      return this .document .getElementById (hash);
   },
   lengthAttribute (attribute, defaultValue, percent)
   {
      // Returns length in pixel.

      if (attribute === null)
         return defaultValue;

      this .parseValue (attribute);

      if (this .double ())
      {
         let value = this .value;

         // Parse unit

         if (SVGParser_Grammar .length .parse (this))
         {
            switch (this .result [0])
            {
               case "em":
                  value *= EM;
                  break;
               case "ex":
                  // TODO
                  break;
               case "px":
                  // We are pixels :)
                  break;
               case "in":
                  value *= INCH / PIXEL;
                  break;
               case "cm":
                  value *= CM / PIXEL;
                  break;
               case "mm":
                  value *= MM / PIXEL;
                  break;
               case "pt":
                  value *= POINT / PIXEL;
                  break;
               case "pc":
                  value *= PICA / PIXEL;
                  break;
               case "%":
               {
                  switch (percent)
                  {
                     case "width":
                        value *= this .viewBox [2] / 100;
                        break;
                     case "height":
                        value *= this .viewBox [3] / 100;
                        break;
                     default:
                        value *= Math .hypot (this .viewBox [2], this .viewBox [3]) / 100;
                        break;
                     }

                  break;
               }
            }
         }

         return value;
      }

      return defaultValue;
   },
   percentAttribute (attribute, defaultValue)
   {
      this .parseValue (attribute);

      if (this .double ())
      {
         let value = this .value;

         // Parse unit

         if (SVGParser_Grammar .percent .parse (this))
            value /= 100;

         return Math_Algorithm .clamp (value, 0, 1);
      }

      return defaultValue;
   },
   pointsAttribute (attribute, points)
   {
      if (attribute === null)
         return false;

      this .parseValue (attribute);

      while (true)
      {
         if (this .double ())
         {
            const x = this .value;

            if (this .comma ())
            {
               if (this .double ())
               {
                  const y = this .value;

                  points .push (new Numbers_Vector3 (x, y, 0));

                  if (this .comma ())
                     continue;
               }
            }
         }

         break;
      }

      return !! points .length;
   },
   dAttribute (attribute, contours)
   {
      if (attribute === null)
         return false;

      this .parseValue (attribute);

      let
         points   = [ ],
         previous = "",
         command  = "",
         relative = false,
         ax       = 0,
         ay       = 0,
         px       = 0,
         py       = 0;

      while (true)
      {
         this .whitespaces ();

         if (!SVGParser_Grammar .path .parse (this))
            break;

         previous = command;
         command  = this .result [0];
         relative = command === command .toLowerCase ();

         switch (command)
         {
            case "m":
            case "M":
            {
               // moveto

               if (points .length > 2)
                  contours .push (Object .assign (points, { closed: false }));

               points = [ ];

               while (true)
               {
                  if (this .double ())
                  {
                     let x = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y = this .value;

                        if (relative)
                        {
                           x += ax;
                           y += ay;
                        }

                        points .push (new Numbers_Vector3 (x, y, 0));

                        ax = x;
                        ay = y;

                        this .comma ();
                        continue;
                     }
                  }

                  break;
               }

               continue;
            }
            case "l":
            case "L":
            {
               // lineto

               while (true)
               {
                  if (this .double ())
                  {
                     let x = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y = this .value;

                        if (relative)
                        {
                           x += ax;
                           y += ay;
                        }

                        points .push (new Numbers_Vector3 (x, y, 0));

                        ax = x;
                        ay = y;

                        this .comma ();
                        continue;
                     }
                  }

                  break;
               }

               continue;
            }
            case "h":
            case "H":
            {
               // horizontal lineto

               while (true)
               {
                  if (this .double ())
                  {
                     let x = this .value;

                     if (relative)
                        x += ax;

                     points .push (new Numbers_Vector3 (x, ay, 0));

                     ax = x;

                     this .comma ();
                     continue;
                  }

                  break;
               }

               continue;
            }
            case "v":
            case "V":
            {
               // vertical lineto

               while (this)
               {
                  if (this .double ())
                  {
                     let y = this .value;

                     if (relative)
                        y += ay;

                     points .push (new Numbers_Vector3 (ax, y, 0));

                     ay = y;

                     this .comma ();
                     continue;
                  }

                  break;
               }

               continue;
            }
            case "q":
            case "Q":
            {
               // quadratic Bézier curveto

               while (true)
               {
                  if (this .double ())
                  {
                     let x1 = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y1 = this .value;

                        this .comma ();

                        if (this .double ())
                        {
                           let x = this .value;

                           this .comma ();

                           if (this .double ())
                           {
                              let y = this .value;

                              if (relative)
                              {
                                 x1 += ax;
                                 y1 += ay;
                                 x  += ax;
                                 y  += ay;
                              }

                              Algorithms_Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, this .BEZIER_STEPS, points);

                              ax = x;
                              ay = y;
                              px = x1;
                              py = y1;

                              this .comma ();
                              continue;
                           }
                        }
                     }
                  }

                  break;
               }

               continue;
            }
            case "t":
            case "T":
            {
               // Shorthand/smooth quadratic Bézier curveto

               while (true)
               {
                  if (this .double ())
                  {
                     let x = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y = this .value;

                        if (relative)
                        {
                           x += ax;
                           y += ay;
                        }

                        let x1, y1;

                        switch (previous)
                        {
                           case 'Q':
                           case 'q':
                           case 'T':
                           case 't':
                           {
                              x1 = ax + (ax - px);
                              y1 = ay + (ay - py);
                              break;
                           }
                           default:
                           {
                              x1 = ax;
                              y1 = ay;
                              break;
                           }
                        }

                        Algorithms_Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, this .BEZIER_STEPS, points);

                        ax = x;
                        ay = y;

                        this .comma ();
                        continue;
                     }
                  }

                  break;
               }

               continue;
            }
            case "c":
            case "C":
            {
               // curveto, cubic Bézier curve

               while (true)
               {
                  if (this .double ())
                  {
                     let x1 = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y1 = this .value;

                        this .comma ();

                        if (this .double ())
                        {
                           let x2 = this .value;

                           this .comma ();

                           if (this .double ())
                           {
                              let y2 = this .value;

                              this .comma ();

                              if (this .double ())
                              {
                                 let x = this .value;

                                 this .comma ();

                                 if (this .double ())
                                 {
                                    let y = this .value;

                                    if (relative)
                                    {
                                       x1 += ax;
                                       y1 += ay;
                                       x2 += ax;
                                       y2 += ay;
                                       x  += ax;
                                       y  += ay;
                                    }

                                    Algorithms_Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, this .BEZIER_STEPS, points);

                                    ax = x;
                                    ay = y;
                                    px = x2;
                                    py = y2;

                                    this .comma ();
                                    continue;
                                 }
                              }
                           }
                        }
                     }
                  }

                  break;
               }

               continue;
            }
            case "s":
            case "S":
            {
               // shorthand/smooth curveto, cubic Bézier curve

               while (true)
               {
                  if (this .double ())
                  {
                     let x2 = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let y2 = this .value;

                        this .comma ();

                        if (this .double ())
                        {
                           let x = this .value;

                           this .comma ();

                           if (this .double ())
                           {
                              let y = this .value;

                              if (relative)
                              {
                                 x2 += ax;
                                 y2 += ay;
                                 x  += ax;
                                 y  += ay;
                              }

                              let x1, y1;

                              switch (previous)
                              {
                                 case 'C':
                                 case 'c':
                                 case 'S':
                                 case 's':
                                 {
                                    x1 = ax + (ax - px);
                                    y1 = ay + (ay - py);
                                    break;
                                 }
                                 default:
                                 {
                                    x1 = ax;
                                    y1 = ay;
                                    break;
                                 }
                              }

                              Algorithms_Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, this .BEZIER_STEPS, points);

                              ax = x;
                              ay = y;
                              px = x2;
                              py = y2;

                              this .comma ();
                              continue;
                           }
                        }
                     }
                  }

                  break;
               }

               continue;
            }
            case "a":
            case "A":
            {
               // elliptical arc

               while (true)
               {
                  if (this .double ())
                  {
                     let rx = this .value;

                     this .comma ();

                     if (this .double ())
                     {
                        let ry = this .value;

                        this .comma ();

                        if (this .double ())
                        {
                           let xAxisRotation = Math_Algorithm .radians (this .value);

                           this .comma ();

                           if (this .int32 ())
                           {
                              let largeArcFlag = this .value;

                              this .comma ();

                              if (this .int32 ())
                              {
                                 let sweepFlag = this .value;

                                 this .comma ();

                                 if (this .double ())
                                 {
                                    let x = this .value;

                                    this .comma ();

                                    if (this .double ())
                                    {
                                       let y = this .value;

                                       if (relative)
                                       {
                                          x += ax;
                                          y += ay;
                                       }

                                       Algorithms_Bezier .arc (ax, ay, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y, this .CIRCLE_STEPS, points);

                                       ax = x;
                                       ay = y;

                                       this .comma ();
                                       continue;
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }

                  break;
               }

               continue;
            }
            case "z":
            case "Z":
            {
               // closepath

               if (points .length > 2)
               {
                  ax = points [0] .x;
                  ay = points [0] .y;

                  contours .push (Object .assign (points, { closed: true }));
               }

               points = [ ];

               this .comma ();
               continue;
            }
         }

         break;
      }

      if (points .length > 2)
         contours .push (Object .assign (points, { closed: false }));

      return !! contours .length;
   },
   transformAttribute (attribute)
   {
      const matrix = new Numbers_Matrix3 ();

      if (attribute === null)
         return matrix;

      this .parseValue (attribute);

      while (true)
      {
         this .comma ();
         this .whitespaces ();

         if (SVGParser_Grammar .matrix .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const a = this .value;

                  if (this .comma ())
                  {
                     if (this .double ())
                     {
                        const b = this .value;

                        if (this .comma ())
                        {
                           if (this .double ())
                           {
                              const c = this .value;

                              if (this .comma ())
                              {
                                 if (this .double ())
                                 {
                                    const d = this .value;

                                    if (this .comma ())
                                    {
                                       if (this .double ())
                                       {
                                          const e = this .value;

                                          if (this .comma ())
                                          {
                                             if (this .double ())
                                             {
                                                const f = this .value;

                                                this .whitespaces ();

                                                if (SVGParser_Grammar .closeParenthesis .parse (this))
                                                {
                                                   matrix .multLeft (new Numbers_Matrix3 (a, b, 0, c, d, 0, e, f, 1));
                                                   continue;
                                                }
                                             }
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
         else if (SVGParser_Grammar .translate .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const tx = this .value;

                  let ty;

                  if (this .comma ())
                  {
                     if (this .double ())
                     {
                        ty = this .value;
                     }
                  }
                  else
                  {
                     ty = 0;
                  }

                  this .whitespaces ();

                  if (SVGParser_Grammar .closeParenthesis .parse (this))
                  {
                     matrix .translate (new Numbers_Vector2 (tx, ty));
                     continue;
                  }
               }
            }
         }
         else if (SVGParser_Grammar .rotate .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const angle = this .value;

                  this .whitespaces ();

                  if (SVGParser_Grammar .closeParenthesis .parse (this))
                  {
                     matrix .rotate (Math_Algorithm .radians (angle));
                     continue;
                  }
                  else
                  {
                     if (this .comma ())
                     {
                        if (this .double ())
                        {
                           const cx = this .value;

                           if (this .comma ())
                           {
                              if (this .double ())
                              {
                                 const cy = this .value;

                                 this .whitespaces ();

                                 if (SVGParser_Grammar .closeParenthesis .parse (this))
                                 {
                                    matrix .translate (new Numbers_Vector2 (cx, cy));
                                    matrix .rotate (Math_Algorithm .radians (angle));
                                    matrix .translate (new Numbers_Vector2 (-cx, -cy));
                                    continue;
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
         else if (SVGParser_Grammar .scale .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const sx = this .value;

                  let sy;

                  if (this .comma ())
                  {
                     if (this .double ())
                     {
                        sy = this .value;
                     }
                  }
                  else
                  {
                     sy = sx;
                  }

                  this .whitespaces ();

                  if (SVGParser_Grammar .closeParenthesis .parse (this))
                  {
                     matrix .scale (new Numbers_Vector2 (sx, sy));
                     continue;
                  }
               }
            }
         }
         else if (SVGParser_Grammar .skewX .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const angle = this .value;

                  this .whitespaces ();

                  if (SVGParser_Grammar .closeParenthesis .parse (this))
                  {
                     matrix .skewX (Math_Algorithm .radians (angle));
                     continue;
                  }
               }
            }
         }
         else if (SVGParser_Grammar .skewY .parse (this))
         {
            this .whitespaces ();

            if (SVGParser_Grammar .openParenthesis .parse (this))
            {
               if (this .double ())
               {
                  const angle = this .value;

                  this .whitespaces ();

                  if (SVGParser_Grammar .closeParenthesis .parse (this))
                  {
                     matrix .skewY (Math_Algorithm .radians (angle));
                     continue;
                  }
               }
            }
         }

         break;
      }

      return matrix;
   },
   styleAttributes (xmlElement)
   {
      const style = Object .assign ({ }, this .styles .at (-1));

      if (this .style .display === "none")
         return false;

      this .styles .push (style);

      for (const attribute of xmlElement .attributes)
         this .parseStyle (attribute .name, attribute .value)

      // Style attribute has higher precedence.

      this .styleAttribute (xmlElement .getAttribute ("style"));

      return true;
   },
   styleAttribute (attribute)
   {
      if (attribute === null)
         return;

      const values = attribute .split (";");

      for (const value of values)
      {
         const pair = value .split (":");

         if (pair .length !== 2)
            continue;

         this .parseStyle (pair [0] .trim (), pair [1] .trim ());
      }
   },
   parseStyle (style, value)
   {
      if (value === "inherit" || value == "unset")
         return;

      this .parseValue (value);

      switch (style)
      {
         case "display":
            this .displayStyle (value);
            break;
         case "fill":
            this .fillStyle (value);
            break;
         case "fill-opacity":
            this .fillOpacityStyle (value);
            break;
         case "fill-rule":
            this .fillRuleStyle (value);
            break;
         case "stroke":
            this .strokeStyle (value);
            break;
         case "stroke-opacity":
            this .strokeOpacityStyle (value);
            break;
         case "stroke-width":
            this .strokeWidthStyle (value);
            break;
         case "opacity":
            this .opacityStyle (value);
            break;
         case "stop-color":
            this .stopColorStyle (value);
            break;
         case "stop-opacity":
            this .stopOpacityStyle (value);
            break;
         case "vector-effect":
            this .vectorEffectStyle (value);
            break;
      }
   },
   displayStyle (value)
   {
      if (value === "default")
      {
         this .style .display = "inline";
         return;
      }

      this .style .display = value;
   },
   fillStyle (value)
   {
      if (value === "default")
      {
         this .style .fillType  = this .styles [0] .fillType;
         this .style .fillColor = this .styles [0] .fillColor;
         this .style .fillURL   = this .styles [0] .fillURL;
         return;
      }

      if (value === "transparent")
      {
         this .style .fillType = "none";
         return;
      }

      if (value === "none")
      {
         this .style .fillType = "none";
         return;
      }

      if (this .urlValue ())
      {
         this .style .fillType = "URL";
         this .style .fillURL  = this .result [1] .trim ();
         return;
      }

      if (this .colorValue (this .styles .at (-1) .fillColor))
      {
         this .style .fillType  = "COLOR";
         this .style .fillColor = this .value .copy ();
         return;
      }
   },
   fillOpacityStyle (value)
   {
      if (value === "default")
      {
         this .style .fillOpacity = this .styles [0] .fillOpacity;
         return;
      }

      if (value === "transparent")
      {
         this .style .fillOpacity = 0;
         return;
      }

      if (this .double ())
      {
         this .style .fillOpacity = Math_Algorithm .clamp (this .value, 0, 1);
         return;
      }
   },
   fillRuleStyle (value)
   {
      if (value === "default")
      {
         this .style .fillRule = this .styles [0] .fillRule;
         return;
      }

      this .style .fillRule = value;
   },
   strokeStyle (value)
   {
      if (value === "default")
      {
         this .style .strokeType  = this .styles [0] .strokeType;
         this .style .strokeColor = this .styles [0] .strokeColor;
         this .style .strokeURL   = this .styles [0] .strokeURL;
         return;
      }

      if (value === "transparent")
      {
         this .style .strokeType = "none";
         return;
      }

      if (value === "none")
      {
         this .style .strokeType = "none";
         return;
      }

      if (this .urlValue ())
      {
         this .style .strokeType = "URL";
         this .style .strokeURL  = this .result [1] .trim ();
         return;
      }

      if (this .colorValue (this .styles .at (-1) .strokeColor))
      {
         this .style .strokeType  = "COLOR";
         this .style .strokeColor = this .value .copy ();
         return;
      }
   },
   strokeOpacityStyle (value)
   {
      if (value === "default")
      {
         this .style .strokeOpacity = this .styles [0] .strokeOpacity;
         return;
      }

      if (value === "transparent")
      {
         this .style .strokeOpacity = 0;
         return;
      }

      if (this .double ())
      {
         this .style .strokeOpacity = Math_Algorithm .clamp (this .value, 0, 1);
         return;
      }
   },
   strokeWidthStyle (value)
   {
      if (value === "default")
      {
         this .style .strokeWidth = this .styles [0] .strokeWidth;
         return;
      }

      if (value === "none")
      {
         this .style .strokeWidth = 0;
         return;
      }

      if (this .double ())
      {
         this .style .strokeWidth = this .lengthAttribute (this .value, 1);
         return;
      }
   },
   opacityStyle (value)
   {
      if (value === "default")
      {
         this .style .opacity = this .styles [0] .opacity;
         return;
      }

      if (value === "transparent")
      {
         this .style .opacity = 0;
         return;
      }

      if (this .double ())
      {
         this .style .opacity = Math_Algorithm .clamp (this .value, 0, 1) * this .styles .at (-1) .opacity;
         return;
      }
   },
   stopColorStyle (value)
   {
      if (value === "default")
      {
         this .style .stopColor = this .styles [0] .stopColor;
         return;
      }

      if (this .colorValue (Numbers_Color4 .BLACK))
      {
         this .style .stopColor = this .value .copy ();
         return;
      }
   },
   stopOpacityStyle (value)
   {
      if (value === "default")
      {
         this .style .stopOpacity = this .styles [0] .stopOpacity;
         return;
      }

      if (value === "transparent")
      {
         this .style .stopOpacity = 0;
         return;
      }

      if (this .double ())
      {
         this .style .stopOpacity = Math_Algorithm .clamp (this .value, 0, 1);
         return;
      }
   },
   vectorEffectStyle (value)
   {
      if (value === "default")
      {
         this .style .vectorEffect = this .styles [0] .vectorEffect;
         return;
      }

      this .style .vectorEffect = value;
   },
   parseValue (value)
   {
      this .input     = value;
      this .lastIndex = 0;
      this .value     = undefined;
   },
   whitespaces ()
   {
      return SVGParser_Grammar .whitespaces .parse (this);
   },
   comma ()
   {
      return !! (this .whitespaces () | SVGParser_Grammar .comma .parse (this));
   },
   int32 ()
   {
      this .whitespaces ();

      if (SVGParser_Grammar .int32 .parse (this))
      {
         this .value = parseInt (this .result [0]);

         return true;
      }

      return false;
   },
   double ()
   {
      this .whitespaces ();

      if (SVGParser_Grammar .double .parse (this))
      {
         this .value = parseFloat (this .result [0]);

         return true;
      }

      if (SVGParser_Grammar .constants .parse (this))
      {
         this .value = this .CONSTANTS .get (this .result [2] .toUpperCase ());

         if (this .result [1] === "-")
            this .value = - this .value;

         return true;
      }

      return false;
   },
   colorValue: (() =>
   {
      const color = new Numbers_Color4 ();

      return function (c)
      {
         if (!SVGParser_Grammar .color .parse (this))
            return false;

         const defaultColor = this .cssColor (c);

         this .value = color .set (... this .convertColor (this .result [0], defaultColor));

         return true;
      };
   })(),
   urlValue ()
   {
      return SVGParser_Grammar .url .parse (this);
   },
   cssColor (c, a = c .a)
   {
      return `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`;
   },
   createTransform (xmlElement, t = Numbers_Vector2 .ZERO, s = Numbers_Vector2 .ONE)
   {
      // Determine matrix.

      const
         scene = this .getExecutionContext (),
         m     = this .transformAttribute (xmlElement .getAttribute ("transform"));

      this .modelMatrix .push ();
      this .modelMatrix .multLeft (Numbers_Matrix4 .fromMatrix3 (m));

      m .translate (t);
      m .scale (s);

      // Create node.

      const
         transformNode    = scene .createNode ("Transform"),
         matrix           = Numbers_Matrix4 .fromMatrix3 (m),
         translation      = new Numbers_Vector3 (),
         rotation         = new Numbers_Rotation4 (),
         scale            = new Numbers_Vector3 (),
         scaleOrientation = new Numbers_Rotation4 ();

      matrix .getTransform (translation, rotation, scale, scaleOrientation);

      transformNode .translation      = translation;
      transformNode .rotation         = rotation;
      transformNode .scale            = scale;
      transformNode .scaleOrientation = scaleOrientation;

      // Set name.

      this .idAttribute (xmlElement .getAttribute ("id"), transformNode);

      // Add node to parent.

      this .groupNodes .at (-1) .children .push (transformNode);

      return transformNode;
   },
   popAll ()
   {
      this .groupNodes  .pop ();
      this .modelMatrix .pop ();
      this .styles      .pop ();
   },
   createFillAppearance (bbox)
   {
      const
         scene          = this .getExecutionContext (),
         appearanceNode = scene .createNode ("Appearance");

      switch (this .style .fillType)
      {
         case "none":
         {
            return null;
         }
         case "COLOR":
         {
            const materialNode = scene .createNode ("UnlitMaterial");

            appearanceNode .material    = materialNode;
            materialNode .emissiveColor = new Numbers_Color3 (... this .style .fillColor);
            materialNode .transparency  = 1 - this .style .fillOpacity * this .style .opacity;

            break;
         }
         case "URL":
         {
            // Gradient

            const
               scene       = this .getExecutionContext (),
               textureNode = scene .createNode ("ImageTexture"),
               url         = this .getFillUrl (this .style .fillURL, bbox);

            // Get image from url.

            if (!url)
               return null;

            textureNode .url               = [url];
            textureNode .textureProperties = this .texturePropertiesNode;
            appearanceNode .texture        = textureNode;

            break;
         }
      }

      return appearanceNode;
   },
   getFillUrl (fillURL, bbox)
   {
      const xmlElement = this .hrefAttribute (fillURL);

      if (!xmlElement)
         return;

      switch (xmlElement .nodeName)
      {
         case "linearGradient":
            return this .linearGradientElementUrl (xmlElement, bbox);

         case "radialGradient":
            return this .radialGradientElementUrl (xmlElement, bbox);

         case "pattern":
            return this .patternUrl (xmlElement);
      }
   },
   createStrokeAppearance ()
   {
      const
         scene          = this .getExecutionContext (),
         appearanceNode = scene .createNode ("Appearance"),
         materialNode   = scene .createNode ("UnlitMaterial");

      appearanceNode .material    = materialNode;
      materialNode .emissiveColor = new Numbers_Color3 (... this .style .strokeColor);
      materialNode .transparency  = 1 - this .style .strokeOpacity * this .style .opacity;

      const strokeWidth = this .vectorEffect === "non-scaling-stroke"
         ? this .style .strokeWidth
         : this .getStokeWidth ();

      if (strokeWidth > 1)
         appearanceNode .lineProperties = this .getLineProperties (strokeWidth);

      return appearanceNode;
   },
   getStokeWidth ()
   {
      const
         modelMatrix = this .modelMatrix .get (),
         strokeWidth = modelMatrix .multDirMatrix (new Numbers_Vector3 (this .style .strokeWidth, this .style .strokeWidth, 0));

      return (strokeWidth .x + strokeWidth .y) / 2;
   },
   getLineProperties (strokeWidth)
   {
      const lineProperties = this .lineProperties .get (strokeWidth);

      if (lineProperties)
      {
         return lineProperties;
      }
      else
      {
         const
            scene          = this .getExecutionContext (),
            lineProperties = scene .createNode ("LineProperties");

         lineProperties .linewidthScaleFactor = strokeWidth;

         this .lineProperties .set (strokeWidth, lineProperties);

         return lineProperties;
      }
   },
   createTextureProperties ()
   {
      const
         scene                 = this .getExecutionContext (),
         texturePropertiesNode = scene .createNode ("TextureProperties");

      texturePropertiesNode .generateMipMaps     = true;
      texturePropertiesNode .minificationFilter  = "NICEST";
      texturePropertiesNode .magnificationFilter = "NICEST";
      texturePropertiesNode .boundaryModeS       = "CLAMP_TO_EDGE";
      texturePropertiesNode .boundaryModeT       = "CLAMP_TO_EDGE";
      texturePropertiesNode .boundaryModeR       = "CLAMP_TO_EDGE";
      texturePropertiesNode .textureCompression  = "DEFAULT";

      return texturePropertiesNode;
   },
   createTextureCoordinate (coordinateNode, bbox, appearance)
   {
      if (!appearance || !appearance .texture)
         return null;

      const
         scene        = this .getExecutionContext (),
         texCoordNode = scene .createNode ("TextureCoordinate"),
         invMatrix    = bbox .matrix .copy () .inverse ();

      for (const point of coordinateNode .point)
         texCoordNode .point .push (invMatrix .multVecMatrix (new Numbers_Vector2 (point .x, point .y)) .add (Numbers_Vector2 .ONE) .divide (2));

      return texCoordNode;
   },
   createTesselator ()
   {
      // Function called for each vertex of tessellator output.

      function vertexCallback (index, triangles)
      {
         triangles .push (index);
      }

      const tessy = new SVGParser_libtess .GluTesselator ();

      tessy .gluTessCallback (SVGParser_libtess .gluEnum .GLU_TESS_VERTEX_DATA, vertexCallback);
      tessy .gluTessNormal (0, 0, 1);

      return tessy;
   },
   triangulatePolygon (contours, coordinateNode)
   {
      // Callback for when segments intersect and must be split.

      const vertices = coordinateNode .point;

      function combineCallback (coords, data, weight)
      {
         const index = vertices .length;

         vertices .push (new Numbers_Vector3 (... coords));

         return index;
      }

      const
         tessy     = this .tessy,
         winding   = this .style .fillRule === "evenodd" ? "GLU_TESS_WINDING_ODD" : "GLU_TESS_WINDING_NONZERO",
         triangles = [ ];

      tessy .gluTessProperty (SVGParser_libtess .gluEnum .GLU_TESS_WINDING_RULE, SVGParser_libtess .windingRule [winding]);
      tessy .gluTessCallback (SVGParser_libtess .gluEnum .GLU_TESS_COMBINE,      combineCallback);
      tessy .gluTessBeginPolygon (triangles);

      for (const points of contours)
      {
         tessy .gluTessBeginContour ();

         for (const [i, point] of points .entries ())
            tessy .gluTessVertex (point, points .index + i);

         tessy .gluTessEndContour ();
      }

      tessy .gluTessEndPolygon ();

      // Array of indices of triangles.
      return triangles;
   },
});

Object .defineProperty (SVGParser .prototype, "style",
{
   get ()
   {
      return this .styles .at (-1);
   },
})

const SVGParser_default_ = SVGParser;
;

/* harmony default export */ const Parser_SVGParser = (Namespace/* default */.A .add ("SVGParser", SVGParser_default_));
;// ./src/x_ite/Parser/GoldenGate.js
/* provided dependency */ var GoldenGate_$ = __webpack_require__(44).A;













class GoldenGate extends Parser_X3DParser
{
   #inputs = new Map ();

   static #parsers = [
      Parser_SVGParser,
      Parser_XMLParser,
      Parser_GLTF2Parser,
      Parser_GLB2Parser,
      Parser_JSONParser,
      Parser_VRMLParser,
      Parser_OBJParser,
      Parser_STLAParser,
      Parser_STLBParser,
      Parser_PLYAParser,
      Parser_PLYBParser,
   ];

   static addParsers (... args)
   {
      this .#parsers .push (... args);
   }

   static removeParsers (... args)
   {
      this .#parsers = this .#parsers .filter (parser => !args .includes (parser));
   }

   static getParsers ()
   {
      return this .#parsers .slice ();
   }

   /**
    * @deprecated Use `GoldenGate.add/remove/getParsers`.
    */
   static get Parser () { return this .#parsers; }

   parseIntoScene (x3dSyntax, resolve, reject)
   {
      for (const Parser of GoldenGate .#parsers)
      {
         try
         {
            const
               parser = new Parser (this .getScene ()),
               input  = this .getInput (parser .getEncoding (), x3dSyntax);

            if (Array .isArray (input) ? input .some (i => i === undefined) : input === undefined)
               continue;

            parser .setInput (input);

            if (!parser .isValid ())
               continue;

            parser .pushExecutionContext (this .getExecutionContext ());
            parser .parseIntoScene (resolve, reject);
            return;
         }
         catch (error)
         {
            if (reject)
               reject (error);
            else
               throw error;

            return;
         }
      }

      if (this .getScene () .worldURL .startsWith ("data:"))
         throw new Error ("Couldn't parse X3D. No suitable file handler found for 'data:' URL.");
      else
         throw new Error (`Couldn't parse X3D. No suitable file handler found for '${this .getScene () .worldURL}'.`);
   }

   getInput (encoding, x3dSyntax)
   {
      if (Array .isArray (encoding))
      {
         return encoding .map (encoding => this .getInput (encoding));
      }
      else
      {
         if (this .#inputs .has (encoding))
            return this .#inputs .get (encoding);

         const input = this .createInput (encoding, x3dSyntax);

         this .#inputs .set (encoding, input);

         return input;
      }
   }

   createInput (encoding, x3dSyntax)
   {
      try
      {
         switch (encoding)
         {
            case "STRING":
            {
               const string = GoldenGate_$.decodeText (x3dSyntax);;

               if (x3dSyntax instanceof ArrayBuffer)
               {
                  if (x3dSyntax .byteLength && !string .length)
                     return undefined;
               }

               return string;
            }
            case "XML":
            {
               const string = this .getInput ("STRING", x3dSyntax);

               return string === undefined ? undefined : GoldenGate_$.parseXML (string);
            }
            case "JSON":
            {
               const string = this .getInput ("STRING", x3dSyntax);

               return string === undefined ? undefined : JSON .parse (string);
            }
            case "ARRAY_BUFFER":
            {
               return x3dSyntax instanceof ArrayBuffer ? x3dSyntax : undefined;
            }
         }
      }
      catch
      {
         return undefined;
      }
   }
}

const GoldenGate_default_ = GoldenGate;
;

/* harmony default export */ const Parser_GoldenGate = (Namespace/* default */.A .add ("GoldenGate", GoldenGate_default_));
;// ./src/x_ite/Components/Layering/LayerSet.js








function LayerSet (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .LayerSet);

   this .addChildObject (Base_X3DConstants .outputOnly, "activeLayerNode",  new x_ite_Fields .SFNode ());

   this .layerNodes = [ new Layering_Layer (executionContext) ];
   this .layerNode0 = this .layerNodes [0];
}

Object .assign (Object .setPrototypeOf (LayerSet .prototype, Core_X3DNode .prototype),
{
   initialize ()
   {
      Core_X3DNode .prototype .initialize .call (this);

      this .layerNode0 .setPrivate (true);
      this .layerNode0 .setup ();
      this .layerNode0 .setLayer0 (true);

      this ._activeLayer .addInterest ("set_activeLayer__", this);
      this ._order       .addInterest ("set_layers__",      this);
      this ._layers      .addInterest ("set_layers__",      this);

      this .set_layers__ ();
   },
   getActiveLayer ()
   {
      return this ._activeLayerNode .getValue ();
   },
   getLayer0 ()
   {
      return this .layerNode0;
   },
   setLayer0 (value)
   {
      this .layerNode0 = value;

      this .set_layers__ ();
   },
   getLayers ()
   {
      return this .layerNodes;
   },
   set_activeLayer__ ()
   {
      let activeLayerNode;

      if (this ._activeLayer .getValue () === 0)
      {
         activeLayerNode = this .layerNode0;
      }
      else
      {
         const index = this ._activeLayer .getValue () - 1;

         if (index >= 0 && index < this ._layers .length)
         {
            activeLayerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, this ._layers [index]);
         }
         else
         {
            activeLayerNode = null;
         }
      }

      if (!activeLayerNode ?._display .getValue ())
         activeLayerNode = null;

      if (this ._activeLayerNode .getValue () !== activeLayerNode)
         this ._activeLayerNode = activeLayerNode;
   },
   set_layers__ ()
   {
      const layers = this ._layers .getValue ();

      for (const layerNode of this .layerNodes)
         layerNode ._display .removeInterest ("set_layers__", this);

      this .layerNodes .length = 0;

      for (let index of this ._order)
      {
         if (index === 0)
         {
            this .layerNode0 ._display .addInterest ("set_layers__", this);
            this .layerNodes .push (this .layerNode0);
         }
         else
         {
            -- index;

            if (index >= 0 && index < layers .length)
            {
               const layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layers [index]);

               if (!layerNode)
                  continue;

               layerNode ._display .addInterest ("set_layers__", this);

               if (layerNode ._display .getValue ())
                  this .layerNodes .push (layerNode);
            }
         }
      }

      this .set_activeLayer__ ();
   },
   bindBindables (viewpointName)
   {
      this .layerNode0 ?.bindBindables (viewpointName);

      for (const node of this ._layers)
      {
         const layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, node);

         layerNode ?.bindBindables (viewpointName);
      }
   },
   traverse (type, renderObject)
   {
      for (const layerNode of this .layerNodes)
         layerNode .traverse (type, renderObject);
   },
});

Object .defineProperties (LayerSet,
{
   ... Core_X3DNode .getStaticProperties ("LayerSet", "Layering", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "activeLayer", new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "order",       new x_ite_Fields .MFInt32 (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "layers",      new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const LayerSet_default_ = LayerSet;
;

/* harmony default export */ const Layering_LayerSet = (Namespace/* default */.A .add ("LayerSet", LayerSet_default_));
;// ./src/x_ite/Execution/X3DWorld.js







function X3DWorld (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DWorld)

   this .addChildObjects (Base_X3DConstants .outputOnly, "activeLayer", new Fields_SFNode (this .layer0));

   this .defaultLayerSet = new Layering_LayerSet (executionContext);
   this .layerSet        = this .defaultLayerSet;
   this .layer0          = new Layering_Layer (executionContext);
}

Object .assign (Object .setPrototypeOf (X3DWorld .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .layerSet .setPrivate (true);
      this .layerSet .setup ();
      this .layerSet .setLayer0 (this .layer0);
      this .layerSet ._activeLayer .addInterest ("set_rootNodes__", this);

      this .getExecutionContext () .getRootNodes () .addInterest ("set_rootNodes__", this);

      this .set_rootNodes__ ();

      this .layer0 .setPrivate (true);
      this .layer0 .setLayer0 (true);
      this .layer0 .setup ();

      this .set_activeLayer__ ();
   },
   getCache ()
   {
      return this .getBrowser () .getBrowserOption ("Cache");
   },
   getLayerSet ()
   {
      return this .layerSet;
   },
   getActiveLayer ()
   {
      return this ._activeLayer .getValue ();
   },
   getLayer0 ()
   {
      return this .layer0;
   },
   set_rootNodes__ ()
   {
      const
         oldLayerSet = this .layerSet,
         rootNodes   = this .getExecutionContext () .getRootNodes ();

      this .layerSet          = this .defaultLayerSet;
      this .layer0 ._children = rootNodes;

      for (const rootNode of rootNodes)
      {
         const layerSet = Base_X3DCast (Base_X3DConstants .LayerSet, rootNode);

         if (layerSet)
            this .layerSet = layerSet;
      }

      if (this .layerSet === oldLayerSet)
         return;

      this .layerSet .setLayer0 (this .layer0);

      oldLayerSet    ._activeLayerNode .removeInterest ("set_activeLayer__", this);
      this .layerSet ._activeLayerNode .addInterest    ("set_activeLayer__", this);

      this .set_activeLayer__ ();
   },
   set_activeLayer__ ()
   {
      this ._activeLayer = this .layerSet .getActiveLayer ();
   },
   bindBindables ()
   {
      // Bind first X3DBindableNodes found in each layer.

      const worldURL = this .getExecutionContext () .getWorldURL ();

      this .layerSet .bindBindables (decodeURIComponent (new URL (worldURL) .hash .substring (1)));
   },
   traverse (type, renderObject)
   {
      this .layerSet .traverse (type, renderObject);
   },
});

for (const key of Object .keys (X3DWorld .prototype))
   Object .defineProperty (X3DWorld .prototype, key, { enumerable: false });

Object .defineProperties (X3DWorld,
{
   typeName:
   {
      value: "X3DWorld",
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DWorld .typeName);

const X3DWorld_default_ = X3DWorld;
;

/* harmony default export */ const Execution_X3DWorld = (Namespace/* default */.A .add ("X3DWorld", X3DWorld_default_));
;// ./src/x_ite/InputOutput/FileLoader.js
/* provided dependency */ var FileLoader_$ = __webpack_require__(44).A;







const foreignMimeType = new Set ([
   "text/html",
   "application/xhtml+xml",
])

function FileLoader (node, { cacheScene = false, dataAsString = true } = { })
{
   Base_X3DObject .call (this);

   this .node             = node;
   this .cacheScene       = cacheScene;
   this .dataAsString     = dataAsString;
   this .browser          = node .getBrowser ();
   this .executionContext = node .getExecutionContext ();
   this .target           = "";
   this .url              = [ ];
   this .fileURL          = new URL (this .getBaseURL ());
   this .controller       = new AbortController ();
}

Object .assign (FileLoader,
{
   sceneCache: new Map (),
   loadDocument (node, url, options)
   {
      return new Promise ((resolve, reject) => new FileLoader (node, options) .loadDocument (url, (data, fileURL) =>
      {
         if (data)
            resolve (data, fileURL);

         reject ();
      }));
   },
});

Object .assign (Object .setPrototypeOf (FileLoader .prototype, Base_X3DObject .prototype),
{
   isPrivate ()
   {
      // Don't count for loading objects.
      return true;
   },
   abort ()
   {
      this .url .length = 0;

      this .controller .abort ();
   },
   getURL ()
   {
      return this .fileURL;
   },
   getBaseURL ()
   {
      if (this .node instanceof Execution_X3DWorld)
         return this .browser .getBaseURL ();

      return this .executionContext .getBaseURL ();
   },
   getTarget (parameters)
   {
      for (const parameter of parameters)
      {
         const pair = parameter .split ("=");

         if (pair .length !== 2)
            continue;

         if (pair [0] === "target")
            return pair [1];
      }

      return "";
   },
   createX3DFromString (worldURL, string = "", resolve, reject)
   {
      try
      {
         const scene = new Execution_X3DScene (this .browser);

         if (!(this .node instanceof Execution_X3DWorld))
            scene .setExecutionContext (this .executionContext);

         scene .setWorldURL (new URL (worldURL, this .getBaseURL ()));
         scene .setup ();

         resolve &&= this .setScene .bind (this, scene, resolve, reject);

         new Parser_GoldenGate (scene) .parseIntoScene (string, resolve, reject);

         return scene;
      }
      catch (error)
      {
         if (reject)
            reject (error);
         else
            throw error;
      }
   },
   setScene (scene, resolve, reject)
   {
      scene ._loadCount .addInterest ("set_loadCount__", this, scene, resolve, reject);
      scene ._loadCount .addEvent ();
   },
   async set_loadCount__ (scene, resolve, reject)
   {
      try
      {
         if (scene ._loadCount .getValue ())
            return;

         scene ._loadCount .removeInterest ("set_loadCount__", this);

         // Wait for instances to be created and events to be processed.

         await this .browser .nextFrame ();

         if (this .cacheScene)
            scene .cache = true;

         this .resolve ?.(scene);
         resolve (scene);
      }
      catch (error)
      {
         if (reject)
            reject (error);
         else
            throw error;
      }

      if (DEVELOPMENT)
      {
         if (this .fileURL .protocol !== "data:")
            console .info (`Done loading scene '${decodeURI (this .fileURL)}'.`);
      }
   },
   createX3DFromURL (url, parameter, callback, bindViewpoint, foreign)
   {
      this .sceneCallback = callback;
      this .bindViewpoint = bindViewpoint;
      this .foreign       = foreign;
      this .target        = this .getTarget (parameter || new x_ite_Fields .MFString ());

      return this .loadDocument (url, this .createX3DFromURLAsync .bind (this, callback));
   },
   createX3DFromURLAsync (callback, data)
   {
      if (data === null)
         callback (null);
      else
         this .createX3DFromString (this .fileURL, data, callback, this .loadDocumentError .bind (this));
   },
   loadDocument (url, callback)
   {
      this .url      = url .slice ();
      this .callback = callback;

      if (url .length === 0)
         return this .loadDocumentError (new Error ("No URL given."));

      this .loadDocumentAsync (String (this .url .shift ()))
         .catch (this .loadDocumentError .bind (this));
   },
   async loadDocumentAsync (url)
   {
      if (!url .length)
         return this .loadDocumentError (new Error ("URL is empty."));

      // Script:
      {
         const result = url .match (/^\s*(?:ecmascript|javascript|vrmlscript)\:/s);

         if (result)
            return await this .callback (url .substring (result [0] .length));
      }

      this .fileURL = new URL (url, this .getBaseURL ());

      // Handle data URLs that are not base64 decoded here:
      if (this .dataAsString)
      {
         const result = url .match (/^\s*data:(.*?)(?:;charset=(.*?))?(?:;(base64))?,/s);

         if (result && result [3] !== "base64")
         {
            // const mimeType = result [1] || "text/plain"";

            let data = url .substring (result [0] .length);

            data = FileLoader_$.try (() => decodeURIComponent (data)) ?? data; // Decode data.
            data = data .replace (/^ï»¿/, "");                      // Remove BOM.

            return await this .callback (data);
         }
      }

      // Bind Viewpoint URLs:

      if (this .fileURL .protocol !== "data:" && this .bindViewpoint)
      {
         const referer = new URL (this .getBaseURL ());

         if (this .fileURL .protocol === referer .protocol &&
             this .fileURL .hostname === referer .hostname &&
             this .fileURL .port     === referer .port &&
             this .fileURL .pathname === referer .pathname &&
             this .fileURL .hash)
         {
            return this .bindViewpoint (decodeURIComponent (this .fileURL .hash .substring (1)));
         }
      }

      // Foreign targets:

      if (this .foreign)
      {
         // Handle target

         if (this .target .length && this .target !== "_self")
            return this .foreign (this .fileURL .href, this .target);

         // Handle well known foreign content depending on extension or if path looks like directory.

         if (this .fileURL .protocol !== "data:" && this .fileURL .href .match (/\.(?:html|htm|xhtml)$/))
            return this .foreign (this .fileURL .href, this .target);
      }

      // Cached scenes:

      if (this .sceneCallback && this .cacheScene && !this .fileURL .search .length)
      {
         const cacheURL = new URL (this .fileURL);

         cacheURL .hash = "";

         const promise = FileLoader .sceneCache .get (cacheURL .href);

         if (promise)
         {
            const scene = await promise;

            scene .setWorldURL (this .fileURL .href);

            return this .sceneCallback (scene);
         }
         else
         {
            const { promise, resolve } = Promise .withResolvers ();

            this .resolve = resolve;

            FileLoader .sceneCache .set (cacheURL .href, promise);
         }
      }

      // Load URL async:

      const
         options  = { cache: this .node .getCache () ? "default" : "reload", signal: this .controller .signal },
         response = this .checkResponse (await fetch (this .fileURL, options)),
         mimeType = response .headers .get ("Content-Type") ?.replace (/;.*$/, "");

      if (this .foreign)
      {
         // console .log (mimeType);

         if (foreignMimeType .has (mimeType))
            return this .foreign (this .fileURL .href, this .target);
      }

      await this .callback (await FileLoader_$.gunzip (await this .getBlob (response)), this .fileURL);
   },
   async getBlob (response)
   {
      const contentLength = parseInt (response .headers .get ("x-file-size"))
         || parseInt (response .headers .get ("content-length"));

      // Check getReader because x_ite-node has no getReader.
      if (!contentLength || !response .body .getReader)
         return await response .blob ();

      const
         browser = this .browser,
         reader  = response .body .getReader (),
         values  = [ ];

      let loadedBytes = 0;

      for (;;)
      {
         const { done, value } = await reader .read ();

         if (done)
            break;

         values .push (value);

         // We count decompressed bytes, but loadedBytes can be number of compressed bytes.
         loadedBytes += value .byteLength;

         browser .setLoadingFractions (this .node, Math .min (loadedBytes / contentLength, 1));
      }

      return await new Blob (values);
   },
   checkResponse (response)
   {
      if (response .ok)
         return response;

      throw Error (response .statusText || response .status);
   },
   loadDocumentError (error)
   {
      // Output error.

      this .printError (error);

      // Try to load next URL.

      if (this .url .length)
      {
         this .loadDocumentAsync (String (this .url .shift ()))
            .catch (this .loadDocumentError .bind (this));
      }
      else
      {
         this .resolve ?.(null);
         this .callback (null);
      }
   },
   printError (error)
   {
      const typeName = this .node instanceof Execution_X3DWorld ? "" : ` for ${this .node .getTypeName ()}`;

      if (this .fileURL .protocol === "data:")
         console .error (`Couldn't load data URL${typeName}.`);
      else
         console .error (`Couldn't load URL '${FileLoader_$.try (() => decodeURI (this .fileURL)) ?? this .fileURL}'${typeName}.`, error);

      console .error (error);
   },
});

for (const key of Object .keys (FileLoader .prototype))
   Object .defineProperty (FileLoader .prototype, key, { enumerable: false });

const FileLoader_default_ = FileLoader;
;

/* harmony default export */ const InputOutput_FileLoader = (Namespace/* default */.A .add ("FileLoader", FileLoader_default_));
;// ./src/x_ite/Prototype/X3DExternProtoDeclaration.js








const
   _proto      = Symbol (),
   _scene      = Symbol (),
   _fileLoader = Symbol ();

function X3DExternProtoDeclaration (executionContext, url)
{
   Prototype_X3DProtoDeclarationNode .call (this, executionContext);
   Networking_X3DUrlObject            .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DExternProtoDeclaration);

   this .addChildObjects (Base_X3DConstants .inputOutput, "load",                 new x_ite_Fields .SFBool (true),
                          Base_X3DConstants .inputOutput, "url",                  url .copy (), // Must be of type MFString.
                          Base_X3DConstants .inputOutput, "autoRefresh",          new x_ite_Fields .SFTime (0),
                          Base_X3DConstants .inputOutput, "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600));
}

Object .assign (Object .setPrototypeOf (X3DExternProtoDeclaration .prototype, Prototype_X3DProtoDeclarationNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Prototype_X3DProtoDeclarationNode .prototype .initialize .call (this);
      Networking_X3DUrlObject            .prototype .initialize .call (this);
   },
   getAppInfo ()
   {
      return this [_proto] ?.getAppInfo ()
         || Prototype_X3DProtoDeclarationNode .prototype .getAppInfo .call (this);
   },
   setAppInfo (value)
   {
      if (this [_proto])
         this [_proto] .setAppInfo (value);
      else
         Prototype_X3DProtoDeclarationNode .prototype .setAppInfo .call (this, value);
   },
   getDocumentation ()
   {
      return this [_proto] ?.getDocumentation ()
         || Prototype_X3DProtoDeclarationNode .prototype .getDocumentation .call (this);
   },
   setDocumentation (value)
   {
      if (this [_proto])
         this [_proto] .setDocumentation (value);
      else
         Prototype_X3DProtoDeclarationNode .prototype .setDocumentation .call (this, value);
   },
   getProtoDeclaration ()
   {
      return this [_proto];
   },
   setProtoDeclaration (proto)
   {
      this [_proto] = proto;

      if (proto)
      {
         this .getFieldDefinitions ()  .assign (proto .getFieldDefinitions ());
         this .getUserDefinedFields () .assign (proto .getUserDefinedFields ());
      }

      this .updateInstances ();
   },
   async loadData ()
   {
      const cache = this .getBrowser () .getBrowserOption ("Cache");

      this [_fileLoader] ?.abort ();

      this [_fileLoader] = new InputOutput_FileLoader (this, { cacheScene: cache })
         .createX3DFromURL (this ._url, null, this .setInternalScene .bind (this));
   },
   getInternalScene ()
   {
      ///  Returns the internal X3DScene of this extern proto, that is loaded from the url given.

      return this [_scene];
   },
   setInternalScene (scene)
   {
      // Remove old scene.

      if (!this [_scene] ?.cache)
         this [_scene] ?.dispose ();

      // Set new scene.

      this [_scene] = scene;

      if (scene)
      {
         const
            browser = this .getBrowser (),
            hash    = new URL (scene .getWorldURL ()) .hash .substring (1),
            proto   = hash ? scene .protos .get (hash) : scene .protos [0];

         if (!proto)
            throw new Error ("PROTO not found.");

         scene .setExecutionContext (scene .cache ? browser .getDefaultScene () : this .getExecutionContext ());
         scene .setLive (true);

         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
         this .setProtoDeclaration (proto);
      }
      else
      {
         this .setLoadState (Base_X3DConstants .FAILED_STATE);
         this .setProtoDeclaration (null);
      }
   },
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "EXTERNPROTO";
      generator .Space ();
      generator .string += this .getName ();
      generator .TidySpace ();
      generator .string += "[";

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length === 0)
      {
         generator .TidySpace ();
      }
      else
      {
         let
            fieldTypeLength   = 0,
            accessTypeLength  = 0;

         for (const field of userDefinedFields)
         {
            fieldTypeLength  = Math .max (fieldTypeLength, field .getTypeName () .length);
            accessTypeLength = Math .max (accessTypeLength, generator .AccessType (field .getAccessType ()) .length);
         }

         generator .TidyBreak ();

         generator .IncIndent ();

         const last = userDefinedFields .at (-1);

         for (const field of userDefinedFields)
         {
            this .toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength);

            if (field === last)
               generator .TidyBreak ();
            else
               generator .Break ();
         }

         generator .DecIndent ();

         generator .Indent ();
      }

      generator .string += "]";
      generator .TidyBreak ();
      generator .Indent ();

      this ._url .toVRMLStream (generator);
   },
   toVRMLStreamUserDefinedField (generator, field, fieldTypeLength, accessTypeLength)
   {
      generator .Indent ();
      generator .string += generator .AccessType (field .getAccessType ()) .padEnd (accessTypeLength, generator .TidySpace ());
      generator .Space ();
      generator .string += field .getTypeName () .padEnd (fieldTypeLength, generator .TidySpace ());
      generator .Space ();
      generator .string += field .getName ();
   },
   toXMLStream (generator)
   {
      generator .openTag ("ExternProtoDeclare");
      generator .attribute ("name", this .getName ());

      generator .Space ();
      generator .string += "url='";

      this ._url .toXMLStream (generator);

      generator .string += "'";

      if (this .getAppInfo ())
         generator .attribute ("appinfo", this .getAppInfo ());

      if (this .getDocumentation ())
         generator .attribute ("documentation", this .getDocumentation ());

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length)
      {
         generator .endTag ();
         generator .IncIndent ();

         for (const field of userDefinedFields)
         {
            generator .openTag ("field");
            generator .attribute ("accessType", generator .AccessType (field .getAccessType ()));
            generator .attribute ("type",       field .getTypeName ());
            generator .attribute ("name",       field .getName ());

            if (field .getAppInfo ())
               generator .attribute ("appinfo", field .getAppInfo ());

            if (field .getDocumentation ())
               generator .attribute ("documentation", field .getDocumentation ());

            generator .closeTag ("field");
            generator .TidyBreak ();
         }

         generator .DecIndent ();
         generator .closingTag ("ExternProtoDeclare");
      }
      else
      {
         generator .closeTag ("ExternProtoDeclare");
      }
   },
   toJSONStream (generator)
   {
      generator .TidyBreak ();
      generator .Indent ();

      generator .beginObject ("ExternProtoDeclare", false, true);
      generator .stringProperty ("@name", this .getName (), false);

      if (this .getAppInfo ())
         generator .stringProperty ("@appinfo", this .getAppInfo ());

      if (this .getDocumentation ())
         generator .stringProperty ("@documentation", this .getDocumentation ());

      // Fields

      const userDefinedFields = this .getUserDefinedFields ();

      if (userDefinedFields .length)
      {
         generator .beginArray ("field");

         for (const field of userDefinedFields)
         {
            generator .beginObject ("", field !== userDefinedFields [0]);
            generator .stringProperty ("@accessType", generator .AccessType (field .getAccessType ()), false);
            generator .stringProperty ("@type",       field .getTypeName ());
            generator .stringProperty ("@name",       field .getName ());

            if (field .getAppInfo ())
               generator .stringProperty ("@appinfo", field .getAppInfo ());

            if (field .getDocumentation ())
               generator .stringProperty ("@documentation", field .getDocumentation ());

            generator .endObject ();
         }

         generator .endArray ();
      }

      // URL

      generator .string += ',';
      generator .TidyBreak ();
      generator .Indent ();
      generator .string += '"';
      generator .string += "@url";
      generator .string += '"';
      generator .string += ':';
      generator .TidySpace ();

      this ._url .toJSONStream (generator);

      // End

      generator .endObject ();
      generator .endObject ();
   },
});

for (const key of Object .keys (X3DExternProtoDeclaration .prototype))
   Object .defineProperty (X3DExternProtoDeclaration .prototype, key, { enumerable: false });

Object .defineProperties (X3DExternProtoDeclaration .prototype,
{
   isExternProto:
   {
      value: true,
      enumerable: true,
   },
   urls:
   {
      get () { return this ._url; },
      enumerable: true,
   },
   loadState:
   {
      get: X3DExternProtoDeclaration .prototype .checkLoadState,
      enumerable: true,
   },
});

Object .defineProperties (X3DExternProtoDeclaration,
{
   typeName:
   {
      value: "X3DExternProtoDeclaration",
      enumerable: true,
   },
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DExternProtoDeclaration .typeName);

const X3DExternProtoDeclaration_default_ = X3DExternProtoDeclaration;
;

/* harmony default export */ const Prototype_X3DExternProtoDeclaration = (Namespace/* default */.A .add ("X3DExternProtoDeclaration", X3DExternProtoDeclaration_default_));
;// ./src/x_ite/Prototype/ExternProtoDeclarationArray.js



function ExternProtoDeclarationArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .name, value]), Prototype_X3DExternProtoDeclaration);
}

Object .setPrototypeOf (ExternProtoDeclarationArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (ExternProtoDeclarationArray .prototype))
   Object .defineProperty (ExternProtoDeclarationArray .prototype, key, { enumerable: false });

Object .defineProperties (ExternProtoDeclarationArray,
{
   typeName:
   {
      value: "ExternProtoDeclarationArray",
      enumerable: true,
   },
});

const ExternProtoDeclarationArray_default_ = ExternProtoDeclarationArray;
;

/* harmony default export */ const Prototype_ExternProtoDeclarationArray = (Namespace/* default */.A .add ("ExternProtoDeclarationArray", ExternProtoDeclarationArray_default_));
;// ./src/x_ite/Prototype/ProtoDeclarationArray.js



function ProtoDeclarationArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .name, value]), Prototype_X3DProtoDeclaration);
}

Object .setPrototypeOf (ProtoDeclarationArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (ProtoDeclarationArray .prototype))
   Object .defineProperty (ProtoDeclarationArray .prototype, key, { enumerable: false });

Object .defineProperties (ProtoDeclarationArray,
{
   typeName:
   {
      value: "ProtoDeclarationArray",
      enumerable: true,
   },
});

const ProtoDeclarationArray_default_ = ProtoDeclarationArray;
;

/* harmony default export */ const Prototype_ProtoDeclarationArray = (Namespace/* default */.A .add ("ProtoDeclarationArray", ProtoDeclarationArray_default_));
;// ./src/x_ite/Routing/X3DRoute.js
/* provided dependency */ var X3DRoute_$ = __webpack_require__(44).A;






const
   X3DRoute_executionContext     = Symbol (),
   _sourceNode           = Symbol (),
   _sourceFieldName      = Symbol (),
   _sourceField          = Symbol (),
   _destinationNode      = Symbol (),
   _destinationFieldName = Symbol (),
   _destinationField     = Symbol (),
   _disposed             = Symbol ();

function X3DRoute (executionContext, sourceNode, sourceFieldName, destinationNode, destinationFieldName)
{
   Base_X3DObject .call (this, executionContext);

   this [X3DRoute_executionContext]     = executionContext;
   this [_sourceNode]           = sourceNode;
   this [_sourceFieldName]      = sourceFieldName;
   this [_destinationNode]      = destinationNode;
   this [_destinationFieldName] = destinationFieldName;

   if (sourceNode instanceof Execution_X3DImportedNode)
      sourceNode .getInlineNode () ._loadState .addInterest ("reconnect", this);

   if (destinationNode instanceof Execution_X3DImportedNode)
      destinationNode .getInlineNode () ._loadState .addInterest ("reconnect", this);

   this .reconnect ();
}

Object .assign (Object .setPrototypeOf (X3DRoute .prototype, Base_X3DObject .prototype),
{
   getExecutionContext ()
   {
      return this [X3DRoute_executionContext];
   },
   getRouteId ()
   {
      return X3DRoute .getRouteId (this [_sourceNode], this [_sourceFieldName], this [_destinationNode], this [_destinationFieldName]);
   },
   getSourceNode ()
   {
      ///  SAI

      if (this [_sourceNode] instanceof Execution_X3DImportedNode)
         return this [_sourceNode] .getExportedNode ();

      return this [_sourceNode];
   },
   getSourceField ()
   {
      ///  SAI

      if (this [_sourceField])
      {
         return this [_sourceField] .getAccessType () === Base_X3DConstants .inputOutput
            ? this [_sourceField] .getName () + "_changed"
            : this [_sourceField] .getName ();
      }
      else
      {
         return this [_sourceFieldName];
      }

   },
   getDestinationNode ()
   {
      ///  SAI

      if (this [_destinationNode] instanceof Execution_X3DImportedNode)
         return this [_destinationNode] .getExportedNode ();

      return this [_destinationNode];
   },
   getDestinationField ()
   {
      ///  SAI

      if (this [_destinationField])
      {
         return this [_destinationField] .getAccessType () === Base_X3DConstants .inputOutput
            ? "set_" + this [_destinationField] .getName ()
            : this [_destinationField] .getName ();
      }
      else
      {
         return this [_destinationFieldName];
      }
   },
   reconnect ()
   {
      try
      {
         this .disconnect ();
         this .connect ();
      }
      catch (error)
      {
         if ((this [_sourceNode] instanceof Core_X3DNode ||
              this [_sourceNode] .getInlineNode () .checkLoadState () === Base_X3DConstants .COMPLETE_STATE) &&
             (this [_destinationNode] instanceof Core_X3DNode ||
              this [_destinationNode] .getInlineNode () .checkLoadState () === Base_X3DConstants .COMPLETE_STATE))
         {
            console .warn (error);
         }
      }
   },
   connect ()
   {
      if (this [_disposed])
         return;

      let firstError, secondError;

      try
      {
         const sourceNode = this [_sourceNode] instanceof Core_X3DNode
            ? this [_sourceNode]
            : this [_sourceNode] .getExportedNode ();

         this [_sourceField] = sourceNode .getField (this [_sourceFieldName]);
      }
      catch (error)
      {
         firstError = error;
      }

      try
      {
         const destinationNode = this [_destinationNode] instanceof Core_X3DNode
            ? this [_destinationNode]
            : this [_destinationNode] .getExportedNode ();

         this [_destinationField] = destinationNode .getField (this [_destinationFieldName]);
      }
      catch (error)
      {
         secondError = error;
      }

      if (this [_sourceField] && this [_destinationField])
      {
         if (this [_sourceField] .getType () !== this [_destinationField] .getType ())
            throw new Error (`Bad ROUTE statement: Source field type must match destination field type of fields named "${this [_sourceField] .getName ()}" and "${this [_destinationField] .getName ()}".`);

         if (!this [_sourceField] .isOutput ())
            throw new Error (`Bad ROUTE statement: Source field "${this [_sourceField] .getName ()}" must be an output.`);

         if (!this [_destinationField] .isInput ())
            throw new Error (`Bad ROUTE statement: Destination field "${this [_destinationField] .getName ()}" must be an input.`);

         this [_sourceField]      .addOutputRoute (this);
         this [_destinationField] .addInputRoute (this);
         this [_sourceField]      .addFieldInterest (this [_destinationField]);
      }
      else
      {
         throw firstError ?? secondError;
      }
   },
   disconnect ()
   {
      this [_sourceField]      ?.removeOutputRoute (this);
      this [_destinationField] ?.removeInputRoute (this);

      if (this [_sourceField] && this [_destinationField])
         this [_sourceField] .removeFieldInterest (this [_destinationField]);

      this [_sourceField]      = null;
      this [_destinationField] = null;
   },
   toVRMLStream (generator)
   {
      if (!generator .ExistsRouteNode (this [_sourceNode]))
         throw new Error (`Source node does not exist in scene graph.`);

      if (!generator .ExistsRouteNode (this [_destinationNode]))
         throw new Error (`Destination node does not exist in scene graph.`);

      const sourceNodeName = this [_sourceNode] instanceof Core_X3DNode
         ? generator .Name (this [_sourceNode])
         : generator .ImportedName (this [_sourceNode]);

      const destinationNodeName = this [_destinationNode] instanceof Core_X3DNode
         ? generator .Name (this [_destinationNode])
         : generator .ImportedName (this [_destinationNode]);

      generator .Indent ();
      generator .string += "ROUTE";
      generator .Space ();
      generator .string += sourceNodeName;
      generator .string += ".";
      generator .string += this .getSourceField ();
      generator .Space ();
      generator .string += "TO";
      generator .Space ();
      generator .string += destinationNodeName;
      generator .string += ".";
      generator .string += this .getDestinationField ();
   },
   toXMLStream (generator)
   {
      if (!generator .ExistsRouteNode (this [_sourceNode]))
         throw new Error (`Source node does not exist in scene graph.`);

      if (!generator .ExistsRouteNode (this [_destinationNode]))
         throw new Error (`Destination node does not exist in scene graph.`);

      const sourceNodeName = this [_sourceNode] instanceof Core_X3DNode
         ? generator .Name (this [_sourceNode])
         : generator .ImportedName (this [_sourceNode]);

      const destinationNodeName = this [_destinationNode] instanceof Core_X3DNode
         ? generator .Name (this [_destinationNode])
         : generator .ImportedName (this [_destinationNode]);

      generator .openTag ("ROUTE");
      generator .attribute ("fromNode",  sourceNodeName);
      generator .attribute ("fromField", this .getSourceField ());
      generator .attribute ("toNode",    destinationNodeName);
      generator .attribute ("toField",   this .getDestinationField ());
      generator .closeTag ("ROUTE");
   },
   toJSONStream (generator)
   {
      if (!generator .ExistsRouteNode (this [_sourceNode]))
         throw new Error (`Source node does not exist in scene graph.`);

      if (!generator .ExistsRouteNode (this [_destinationNode]))
         throw new Error (`Destination node does not exist in scene graph.`);

      const sourceNodeName = this [_sourceNode] instanceof Core_X3DNode
         ? generator .Name (this [_sourceNode])
         : generator .ImportedName (this [_sourceNode]);

      const destinationNodeName = this [_destinationNode] instanceof Core_X3DNode
         ? generator .Name (this [_destinationNode])
         : generator .ImportedName (this [_destinationNode]);

      generator .TidyBreak ();
      generator .Indent ();

      generator .beginObject ("ROUTE", false, true);

      generator .stringProperty ("@fromNode",  sourceNodeName, false);
      generator .stringProperty ("@fromField", this .getSourceField ());
      generator .stringProperty ("@toNode",    destinationNodeName);
      generator .stringProperty ("@toField",   this .getDestinationField ());

      generator .endObject ();
      generator .endObject ();
   },
   dispose ()
   {
      if (this [_disposed])
         return;

      this [_disposed] = true;

      this .disconnect ();

      if (this [_sourceNode] instanceof Execution_X3DImportedNode)
         this [_sourceNode] .getInlineNode () ._loadState .removeInterest ("reconnect", this);

      if (this [_destinationNode] instanceof Execution_X3DImportedNode)
         this [_destinationNode] .getInlineNode () ._loadState .removeInterest ("reconnect", this);

      this [X3DRoute_executionContext] .deleteRoute (this);

      Base_X3DObject .prototype .dispose .call (this);
   }
});

for (const key of Object .keys (X3DRoute .prototype))
   Object .defineProperty (X3DRoute .prototype, key, { enumerable: false });

Object .defineProperties (X3DRoute .prototype,
{
   sourceNode:
   {
      get ()
      {
         return Fields_SFNodeCache .get (this .getSourceNode ());
      },
      enumerable: true,
   },
   sourceField:
   {
      get: X3DRoute .prototype .getSourceField,
      enumerable: true,
   },
   destinationNode:
   {
      get ()
      {
         return Fields_SFNodeCache .get (this .getDestinationNode ());
      },
      enumerable: true,
   },
   destinationField:
   {
      get: X3DRoute .prototype .getDestinationField,
      enumerable: true,
   },
});

Object .defineProperties (X3DRoute,
{
   typeName:
   {
      value: "X3DRoute",
      enumerable: true,
   },
});

Object .assign (X3DRoute,
{
   getRouteId (sourceNode, sourceFieldName, destinationNode, destinationFieldName)
   {
      const sourceField = sourceNode instanceof Core_X3DNode
         ? sourceNode .getField (sourceFieldName)
         : X3DRoute_$.try (() => sourceNode .getExportedNode () .getField (sourceFieldName));

      const destinationField = destinationNode instanceof Core_X3DNode
         ? destinationNode .getField (destinationFieldName)
         : X3DRoute_$.try (() => destinationNode .getExportedNode () .getField (destinationFieldName));

      if (sourceField)
      {
         sourceFieldName = sourceField .getName ();

         if (sourceField .getAccessType () === Base_X3DConstants .inputOutput)
            sourceFieldName += "_changed";
      }

      if (destinationField)
      {
         destinationFieldName = destinationField .getName ();

         if (destinationField .getAccessType () === Base_X3DConstants .inputOutput)
            destinationFieldName = "set_" + destinationFieldName;
      }

      return `${sourceNode .getId ()}.${sourceFieldName}.${destinationNode .getId ()}.${destinationFieldName}`;
   },
});

const X3DRoute_default_ = X3DRoute;
;

/* harmony default export */ const Routing_X3DRoute = (Namespace/* default */.A .add ("X3DRoute", X3DRoute_default_));
;// ./src/x_ite/Routing/RouteArray.js



function RouteArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .getId (), value]), Routing_X3DRoute);
}

Object .setPrototypeOf (RouteArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (RouteArray .prototype))
   Object .defineProperty (RouteArray .prototype, key, { enumerable: false });

Object .defineProperties (RouteArray,
{
   typeName:
   {
      value: "RouteArray",
      enumerable: true,
   },
});

const RouteArray_default_ = RouteArray;
;

/* harmony default export */ const Routing_RouteArray = (Namespace/* default */.A .add ("RouteArray", RouteArray_default_));
;// ./src/x_ite/Execution/X3DExecutionContext.js


















const
   _outerNode     = Symbol (),
   _namedNodes    = Symbol (),
   _importedNodes = Symbol (),
   _protos        = Symbol (),
   _externprotos  = Symbol (),
   _routes        = Symbol ();

function X3DExecutionContext (executionContext, outerNode = null, browser = executionContext .getBrowser ())
{
   Base_X3DBaseNode .call (this, executionContext, browser);

   this .addType (Base_X3DConstants .X3DExecutionContext)

   this .addChildObjects (Base_X3DConstants .initializeOnly, "rootNodes",          new x_ite_Fields .MFNode (),
                          Base_X3DConstants .inputOutput,    "countPrimitives",    new x_ite_Fields .SFBool (true),
                          Base_X3DConstants .outputOnly,     "worldInfos",         new x_ite_Fields .MFNode (),
                          Base_X3DConstants .outputOnly,     "sceneGraph_changed", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly,     "bbox_changed",       new x_ite_Fields .SFTime ())

   this ._rootNodes .setPrivate (false);
   this ._rootNodes .collectCloneCount = () => 1;

   if (executionContext)
   {
      this ._countPrimitives    .addReference     (executionContext ._countPrimitives);
      this ._sceneGraph_changed .addFieldInterest (executionContext ._sceneGraph_changed);
      this ._bbox_changed       .addFieldInterest (executionContext ._bbox_changed);
   }

   this [_outerNode]       = outerNode;
   this [_namedNodes]      = new Execution_NamedNodesArray ();
   this [_importedNodes]   = new Execution_ImportedNodesArray ();
   this [_protos]          = new Prototype_ProtoDeclarationArray ();
   this [_externprotos]    = new Prototype_ExternProtoDeclarationArray ();
   this [_routes]          = new Routing_RouteArray ();

   this [_namedNodes]     .addParent (this);
   this [_importedNodes]  .addParent (this);
   this [_protos]         .addParent (this);
   this [_externprotos]   .addParent (this);
   this [_routes]         .addParent (this);
}

Object .assign (Object .setPrototypeOf (X3DExecutionContext .prototype, Base_X3DBaseNode .prototype),
{
   getLocalScene ()
   {
      return this .getExecutionContext () .getLocalScene ();
   },
   setExecutionContext (executionContext)
   {
      if (this .getExecutionContext ())
      {
         this ._countPrimitives .removeReference (this .getExecutionContext () ._countPrimitives);
         this ._sceneGraph_changed .removeFieldInterest (this .getExecutionContext () ._sceneGraph_changed);
         this ._bbox_changed       .removeFieldInterest (this .getExecutionContext () ._bbox_changed);
      }

      Base_X3DBaseNode .prototype .setExecutionContext .call (this, executionContext);

      if (executionContext)
      {
         this ._countPrimitives .addReference (executionContext ._countPrimitives);
         this ._sceneGraph_changed .addFieldInterest (executionContext ._sceneGraph_changed);
         this ._bbox_changed       .addFieldInterest (executionContext ._bbox_changed);
      }
   },
   getOuterNode ()
   {
      // Can be either of type X3DProtoDeclaration or X3DPrototypeInstance, or null.
      return this [_outerNode];
   },
   getCountPrimitives ()
   {
      return this ._countPrimitives .getValue ();
   },
   setCountPrimitives (value)
   {
      this ._countPrimitives = (this .getExecutionContext () ?.getCountPrimitives () ?? true) && value;
   },
   getBBox (bbox, shadows)
   {
      const nodes = Array .from (this .getRootNodes (), node => Base_X3DCast (Base_X3DConstants .X3DBoundedObject, node))
         .filter (node => node);

      return Grouping_X3DBoundedObject .prototype .getBBox (nodes, bbox, shadows);
   },
   getSpecificationVersion ()
   {
      return this .getExecutionContext () .getSpecificationVersion ();
   },
   getEncoding ()
   {
      return this .getExecutionContext () .getEncoding ();
   },
   getWorldURL ()
   {
      return this .getExecutionContext () .getWorldURL ();
   },
   getBaseURL ()
   {
      return this .getExecutionContext () .getBaseURL ();
   },
   getProfile ()
   {
      return this .getExecutionContext () .getProfile ();
   },
   hasComponent (name, level)
   {
      return this .getExecutionContext () .hasComponent (name, level);
   },
   getComponents ()
   {
      return this .getExecutionContext () .getComponents ();
   },
   fromUnit (category, value)
   {
      return this .getExecutionContext () .fromUnit (category, value);
   },
   toUnit (category, value)
   {
      return this .getExecutionContext () .toUnit (category, value);
   },
   getUnits ()
   {
      return this .getExecutionContext () .getUnits ();
   },
   createNode (typeName, setup = true /* non-public argument */)
   {
      typeName = String (typeName);

      if (setup === false)
      {
         const ConcreteNode = this .getBrowser () .getConcreteNodes () .get (typeName);

         if (!ConcreteNode)
            return null;

         // There must be an error if the specification range does not match,
         // otherwise protos with the same name will be overwritten, e.g. Delay.

         const { from, to } = ConcreteNode .specificationRange;

         if (this .getSpecificationVersion () < from || this .getSpecificationVersion () > to)
            return null;

         if (!this .hasComponent (ConcreteNode .componentInfo .name))
            console .warn (`Node type '${typeName}' does not match component/profile statements in '${this .getWorldURL ()}'.`);

         return new ConcreteNode (this);
      }
      else
      {
         const ConcreteNode = this .getBrowser () .getConcreteNodes () .get (typeName);

         if (!ConcreteNode)
            throw new Error (`Unknown node type '${typeName}'.`);

         // There must be an error if the specification range does not match,
         // otherwise protos with the same name will be overwritten, e.g. Delay.

         const { from, to } = ConcreteNode .specificationRange;

         if (this .getSpecificationVersion () < from || this .getSpecificationVersion () > to)
            throw new Error (`Node type '${typeName}' does not match specification version in '${this .getWorldURL ()}.`);

         if (!this .hasComponent (ConcreteNode .componentInfo .name))
            console .warn (`Node type '${typeName}' does not match component/profile statements in '${this .getWorldURL ()}'.`);

         const baseNode = new ConcreteNode (this);

         baseNode .setup ();

         return Fields_SFNodeCache .get (baseNode);
      }
   },
   createProto (name, setup = true /* non-public argument */)
   {
      name = String (name);

      for (let executionContext = this;;)
      {
         const protoNode = executionContext .protos .get (name)
            ?? executionContext .externprotos .get (name);

         if (protoNode)
            return protoNode .createInstance (this, setup);

         if (executionContext .isScene)
            break;

         executionContext = executionContext .getExecutionContext ();
      }

      if (setup === false)
         return null;

      throw new Error (`Unknown proto or externproto type '${name}'.`);
   },
   addNamedNode (name, node)
   {
      name = String (name);
      node = Base_X3DCast (Base_X3DConstants .X3DNode, node, false);

      if (!node)
         throw new Error ("Couldn't add named node: node must be of type X3DNode.");

      if (node .getExecutionContext () !== this)
         throw new Error ("Couldn't add named node: node does not belong to this execution context.");

      if (name .length === 0)
         throw new Error ("Couldn't add named node: node name is empty.");

      if (this [_namedNodes] .has (name))
         throw new Error (`Couldn't add named node: node named '${name}' is already in use.`);

      if (this [_namedNodes] .get (node .getName ()) ?.getValue () === node)
         throw new Error (`Couldn't add named node: node named '${node .getName ()}' already has a name.`);

      // Add named node.

      node .setName (name);

      this [_namedNodes] .add (name, Fields_SFNodeCache .get (node));
   },
   updateNamedNode (name, node)
   {
      name = String (name);
      node = Base_X3DCast (Base_X3DConstants .X3DNode, node, false);

      if (!node)
         throw new Error ("Couldn't update named node: node must be of type X3DNode.");

      if (node .getExecutionContext () !== this)
         throw new Error ("Couldn't update named node: node does not belong to this execution context.");

      if (name .length === 0)
         throw new Error ("Couldn't update named node: node name is empty.");

      // Remove named node.

      this .removeNamedNode (name);

      // Update named node.

      this [_namedNodes] .update (node .getName (), name, Fields_SFNodeCache .get (node));

      node .setName (name);

      this ._namedNodes_changed = Date .now () / 1000;
   },
   removeNamedNode (name)
   {
      name = String (name);

      const node = this [_namedNodes] .get (name);

      node ?.getValue () ?.setName ("");

      this [_namedNodes] .remove (name);

      this ._namedNodes_changed = Date .now () / 1000;
   },
   getNamedNode (name)
   {
      name = String (name);

      const node = this [_namedNodes] .get (name);

      if (node ?.getValue ())
         return node;

      throw new Error (`Named node '${name}' not found.`);
   },
   getNamedNodes ()
   {
      return this [_namedNodes];
   },
   getUniqueName (name)
   {
      return getUniqueName (this [_namedNodes], name);
   },
   addImportedNode (inlineNode, exportedName, importedName = exportedName, description = "")
   {
      exportedName = String (exportedName);
      importedName = String (importedName);

      if (this [_importedNodes] .has (importedName))
         throw new Error (`Couldn't add imported node: imported name '${importedName}' already in use.`);

      this .updateImportedNode (inlineNode, exportedName, importedName, description);
   },
   updateImportedNode (inlineNode, exportedName, importedName, description = "")
   {
      inlineNode   = Base_X3DCast (Base_X3DConstants .Inline, inlineNode, false);
      exportedName = String (exportedName);
      importedName = importedName === undefined ? exportedName : String (importedName);
      description  = String (description);

      if (!inlineNode)
         throw new Error ("Node must be of type Inline node.");

      if (inlineNode .getExecutionContext () !== this)
         throw new Error ("Couldn't update imported node: Inline node does not belong to this execution context.");

      if (exportedName .length === 0)
         throw new Error ("Couldn't update imported node: exported name is empty.");

      if (importedName .length === 0)
         throw new Error ("Couldn't update imported node: imported name is empty.");

      // Update imported node.

      this .removeImportedNode (importedName);

      const importedNode = new Execution_X3DImportedNode (this, inlineNode, exportedName, importedName, description);

      this [_importedNodes] .add (importedName, importedNode);

      importedNode .updateExportedNode ();

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   renameImportedNode (oldImportedName, newImportedName)
   {
      const importedNode = this [_importedNodes] .get (oldImportedName);

      if (!importedNode)
         throw new Error ("Couldn't rename imported node: imported node does not exists.");

      if (this [_importedNodes] .get (newImportedName))
         throw new Error ("Couldn't rename imported node: new imported name does already exists.");

      importedNode [Symbol .for ("X_ITE.X3DImportedNode.setImportName")] (newImportedName);

      this [_importedNodes] .update (oldImportedName, newImportedName, importedNode);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   removeImportedNode (importedName)
   {
      importedName = String (importedName);

      const importedNode = this [_importedNodes] .get (importedName);

      if (!importedNode)
         return;

      importedNode .dispose ();

      this [_importedNodes] .remove (importedName);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   getImportedNode (importedName)
   {
      importedName = String (importedName);

      const importedNode = this [_importedNodes] .get (importedName);

      if (importedNode)
         return importedNode .exportedNode;

      throw new Error (`Imported node '${importedName}' not found.`);
   },
   getImportedNodes ()
   {
      return this [_importedNodes];
   },
   getUniqueImportName (name)
   {
      return getUniqueName (this [_importedNodes], name);
   },
   getLocalNode (name)
   {
      name = String (name);

      try
      {
         return this .getNamedNode (name);
      }
      catch
      {
         const importedNode = this [_importedNodes] .get (name);

         if (importedNode)
            return importedNode;

         throw new Error (`Unknown named or imported node '${name}'.`);
      }
   },
   /**
    *
    * @param {SFNode|X3DNode|X3DImportedNode} node
    * @returns either an X3DImportedNode if possible or X3DNode
    */
   getLocalizedNode (node)
   {
      node = Base_X3DCast (Base_X3DConstants .X3DNode, node, false)
         ?? (node instanceof Execution_X3DImportedNode ? node : null);

      if (!node)
         throw new Error ("Couldn't get localized node: node must be of type X3DNode.");

      if (node .getExecutionContext () === this)
      {
         if (node instanceof Core_X3DImportedNodeProxy)
            return node .getImportedNode ();

         return node;
      }

      for (const importedNode of this [_importedNodes])
      {
         try
         {
            if (importedNode .getSharedNode () === node)
               return importedNode;
         }
         catch
         { }
      }

      return node;
   },
   setRootNodes ()
   { },
   getRootNodes ()
   {
      return this ._rootNodes;
   },
   getProtoDeclaration (name)
   {
      name = String (name);

      const proto = this [_protos] .get (name);

      if (proto)
         return proto;

      throw new Error (`Proto declaration '${name}' not found.`);
   },
   addProtoDeclaration (name, proto)
   {
      name = String (name);

      if (!(proto instanceof Prototype_X3DProtoDeclaration))
         throw new Error ("Couldn't add proto declaration: proto must be of type X3DProtoDeclaration.");

      if (proto .getExecutionContext () !== this)
         throw new Error ("Couldn't add proto declaration: proto does not belong to this execution context.");

      if (this [_protos] .has (name))
         throw new Error (`Couldn't add proto declaration: proto '${name}' already in use.`);

      if (this [_protos] .get (proto .getName ()) === proto)
         throw new Error (`Couldn't add proto declaration: proto '${proto .getName ()}' already added.`);

      if (name .length === 0)
         throw new Error ("Couldn't add proto declaration: proto name is empty.");

      if (this [_externprotos] .has (name))
         console .warn (`Added proto '${name}' will override extern proto with same name.`);

      if (this .getBrowser () .getConcreteNodes () .has (name))
         console .warn (`Added proto '${name}', but will not override built-in node of same type if profile/components include this node.`);

      this [_protos] .add (name, proto);
      proto .setName (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   updateProtoDeclaration (name, proto)
   {
      name = String (name);

      if (!(proto instanceof Prototype_X3DProtoDeclaration))
         throw new Error ("Couldn't update proto declaration: proto must be of type X3DProtoDeclaration.");

      if (proto .getExecutionContext () !== this)
         throw new Error ("Couldn't update proto declaration: proto does not belong to this execution context.");

      if (name .length === 0)
         throw new Error ("Couldn't update proto declaration: proto name is empty.");

      if (this [_externprotos] .has (name))
         console .warn (`Added proto '${name}' will override extern proto with same name.`);

      if (this .getBrowser () .getConcreteNodes () .has (name))
         console .warn (`Added proto '${name}', but will not override built-in node of same type if profile/components include this node.`);

      this [_protos] .update (proto .getName (), name, proto);
      proto .setName (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   removeProtoDeclaration (name)
   {
      name = String (name);

      this [_protos] .remove (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   getProtoDeclarations ()
   {
      return this [_protos];
   },
   getUniqueProtoName (name)
   {
      return getUniqueName (this [_protos], name);
   },
   getExternProtoDeclaration (name)
   {
      name = String (name);

      const externproto = this [_externprotos] .get (name);

      if (externproto)
         return externproto;

      throw new Error (`Extern proto declaration '${name}' not found.`);
   },
   addExternProtoDeclaration (name, externproto)
   {
      name = String (name);

      if (!(externproto instanceof Prototype_X3DExternProtoDeclaration))
         throw new Error ("Couldn't add extern proto declaration: extern proto must be of type X3DExternProtoDeclaration.");

      if (externproto .getExecutionContext () !== this)
         throw new Error ("Couldn't add extern proto declaration: extern proto does not belong to this execution context.");

      if (this [_externprotos] .has (name))
         throw new Error (`Couldn't add extern proto declaration: extern proto '${name}' already in use.`);

      if (this [_externprotos] .get (externproto .getName ()) === externproto)
         throw new Error (`Couldn't add extern proto declaration: extern proto '${externproto .getName ()}' already added.`);

      if (name .length === 0)
         throw new Error ("Couldn't add extern proto declaration: extern proto name is empty.");

      if (this [_protos] .has (name))
         console .warn (`Added extern proto '${name}' will be overridden by proto with same name.`);

      if (this .getBrowser () .getConcreteNodes () .has (name))
         console .warn (`Added extern proto '${name}', but will not override built-in node of same type if profile/components include this node.`);

      this [_externprotos] .add (name, externproto);
      externproto .setName (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   updateExternProtoDeclaration (name, externproto)
   {
      name = String (name);

      if (!(externproto instanceof Prototype_X3DExternProtoDeclaration))
         throw new Error ("Couldn't update extern proto declaration: extern proto must be of type X3DExternProtoDeclaration.");

      if (externproto .getExecutionContext () !== this)
         throw new Error ("Couldn't update extern proto declaration: extern proto does not belong to this execution context.");

      if (name .length === 0)
         throw new Error ("Couldn't update extern proto declaration: extern proto name is empty.");

      if (this [_protos] .has (name))
         console .warn (`Added extern proto '${name}' will be overridden by proto with same name.`);

      if (this .getBrowser () .getConcreteNodes () .has (name))
         console .warn (`Added extern proto '${name}', but will not override built-in node of same type if profile/components include this node.`);

      this [_externprotos] .update (externproto .getName (), name, externproto);
      externproto .setName (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   removeExternProtoDeclaration (name)
   {
      name = String (name);

      this [_externprotos] .remove (name);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   getExternProtoDeclarations ()
   {
      return this [_externprotos];
   },
   getUniqueExternProtoName (name)
   {
      return getUniqueName (this [_externprotos], name);
   },
   addRoute (sourceNode, sourceField, destinationNode, destinationField)
   {
      try
      {
         // Normalize arguments.

         const
            importedSourceNode      = sourceNode      instanceof Execution_X3DImportedNode ? sourceNode      : null,
            importedDestinationNode = destinationNode instanceof Execution_X3DImportedNode ? destinationNode : null;

         sourceNode       = Base_X3DCast (Base_X3DConstants .X3DNode, sourceNode, false) ?? importedSourceNode;
         sourceField      = String (sourceField);
         destinationNode  = Base_X3DCast (Base_X3DConstants .X3DNode, destinationNode, false) ?? importedDestinationNode;
         destinationField = String (destinationField);

         // Check nodes.

         if (!sourceNode)
            throw new Error ("source node must be of type X3DNode.");

         if (!destinationNode)
            throw new Error ("destination node must be of type X3DNode.");

         // Resolve imported source and destination node.

         sourceNode      = this .getLocalizedNode (sourceNode);
         destinationNode = this .getLocalizedNode (destinationNode);

         // Add route.

         const
            id    = Routing_X3DRoute .getRouteId (sourceNode, sourceField, destinationNode, destinationField),
            route = this [_routes] .get (id);

         if (route)
         {
            return route;
         }
         else
         {
            const route = new Routing_X3DRoute (this, sourceNode, sourceField, destinationNode, destinationField);

            this [_routes] .add (id, route);

            return route;
         }
      }
      catch (error)
      {
         throw new Error (`Bad ROUTE specification: ${error .message}`);
      }
   },
   deleteRoute (route)
   {
      // Case: sourceNode, sourceField, destinationNode, destinationField.
      if (arguments .length === 4)
         route = this .getRoute (... arguments);

      if (!(route instanceof Routing_X3DRoute))
         return;

      if (this [_routes] .get (route .getRouteId ()) !== route)
         return;

      this [_routes] .remove (route .getRouteId ());

      route .dispose ();
   },
   getRoute (sourceNode, sourceField, destinationNode, destinationField)
   {
      // Normalize arguments.

      const
         importedSourceNode      = sourceNode      instanceof Execution_X3DImportedNode ? sourceNode      : null,
         importedDestinationNode = destinationNode instanceof Execution_X3DImportedNode ? destinationNode : null;

      sourceNode       = Base_X3DCast (Base_X3DConstants .X3DNode, sourceNode, false) ?? importedSourceNode;
      sourceField      = String (sourceField)
      destinationNode  = Base_X3DCast (Base_X3DConstants .X3DNode, destinationNode, false) ?? importedDestinationNode;
      destinationField = String (destinationField)

      // Check nodes.

      if (!sourceNode)
         throw new Error ("Bad ROUTE specification: sourceNode must be of type X3DNode.");

      if (!destinationNode)
         throw new Error ("Bad ROUTE specification: destinationNode must be of type X3DNode.");

      // Resolve imported source and destination node.

      sourceNode      = this .getLocalizedNode (sourceNode);
      destinationNode = this .getLocalizedNode (destinationNode);

      // Return route.

      return this [_routes] .get (Routing_X3DRoute .getRouteId (sourceNode, sourceField, destinationNode, destinationField));
   },
   getRoutes ()
   {
      return this [_routes];
   },
   getWorldInfos ()
   {
      return this ._worldInfos;
   },
   addWorldInfo (worldInfoNode)
   {
      this ._worldInfos .push (worldInfoNode);
   },
   removeWorldInfo (worldInfoNode)
   {
      for (const [i, worldInfo] of this ._worldInfos .entries ())
      {
         if (worldInfo .getValue () !== worldInfoNode)
            continue;

         this ._worldInfos .splice (i, 1);
         break;
      }
   },
   toVRMLStream (generator)
   {
      generator .PushExecutionContext (this);
      generator .NamedNodes (this .getNamedNodes ());
      generator .ImportedNodes (this .getImportedNodes ());

      // Output extern protos

      this .getExternProtoDeclarations () .toVRMLStream (generator);

      // Output protos

      this .getProtoDeclarations () .toVRMLStream (generator);

      // Output root nodes

      const
         rootNodes = this .getRootNodes (),
         last      = rootNodes .length - 1;

      for (const [i, rootNode] of rootNodes .entries ())
      {
         generator .Indent ();

         if (rootNode)
            rootNode .toVRMLStream (generator);
         else
            generator .NULL ();

         generator .TidyBreak ();

         if (i !== last)
            generator .TidyBreak ();
      }

      // Output imported nodes

      const importedNodes = this .getImportedNodes ();

      if (importedNodes .length)
      {
         generator .TidyBreak ();

         importedNodes .toVRMLStream (generator);
      }

      // Output routes

      const routes = this .getRoutes ();

      if (routes .length)
      {
         generator .TidyBreak ();

         routes .toVRMLStream (generator);
      }

      generator .PopExecutionContext ();
   },
   toXMLStream (generator)
   {
      generator .PushExecutionContext (this);
      generator .NamedNodes (this .getNamedNodes ());
      generator .ImportedNodes (this .getImportedNodes ());

      // Output extern protos

      this .getExternProtoDeclarations () .toXMLStream (generator);

      // Output protos

      this .getProtoDeclarations () .toXMLStream (generator);

      // Output root nodes

      const rootNodes = this .getRootNodes ();

      if (rootNodes .length)
      {
         rootNodes .toXMLStream (generator);

         generator .TidyBreak ();
      }

      // Output imported nodes

      this .getImportedNodes () .toXMLStream (generator);

      // Output routes

      this .getRoutes () .toXMLStream (generator);

      generator .PopExecutionContext ();
   },
   toJSONStream (generator)
   {
      generator .PushExecutionContext (this);
      generator .NamedNodes (this .getNamedNodes ());
      generator .ImportedNodes (this .getImportedNodes ());

      let comma = false;

      comma = this .getExternProtoDeclarations () .toJSONStream (generator, comma);
      comma = this .getProtoDeclarations ()       .toJSONStream (generator, comma);

      // Root nodes

      for (const rootNode of this .getRootNodes ())
      {
         if (comma)
            generator .string += ',';

         generator .TidyBreak ();
         generator .Indent ();

         if (rootNode)
            rootNode .toJSONStream (generator);
         else
            generator .string += "null";

         comma = true;
      }

      this .getImportedNodes () .toJSONStream (generator, comma);
      this .getRoutes ()        .toJSONStream (generator, comma);

      generator .PopExecutionContext ();

      return comma;
   },
   dispose ()
   {
      for (const route of Array .from (this [_routes]))
         this .deleteRoute (route);

      this [_outerNode] = null;
      this [_namedNodes]    .clear ();
      this [_importedNodes] .clear ();
      this [_protos]        .clear ();
      this [_externprotos]  .clear ();
      this [_routes]        .clear ();

      Base_X3DBaseNode .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DExecutionContext .prototype))
   Object .defineProperty (X3DExecutionContext .prototype, key, { enumerable: false });

Object .defineProperties (X3DExecutionContext .prototype,
{
   isScene:
   {
      value: false,
      enumerable: true,
   },
   specificationVersion:
   {
      get: X3DExecutionContext .prototype .getSpecificationVersion,
      enumerable: true,
   },
   encoding:
   {
      get: X3DExecutionContext .prototype .getEncoding,
      enumerable: true,
   },
   profile:
   {
      get: X3DExecutionContext .prototype .getProfile,
      enumerable: true,
   },
   components:
   {
      get: X3DExecutionContext .prototype .getComponents,
      enumerable: true,
   },
   worldURL:
   {
      get: X3DExecutionContext .prototype .getWorldURL,
      enumerable: true,
   },
   baseURL:
   {
      get: X3DExecutionContext .prototype .getBaseURL,
      enumerable: true,
   },
   units:
   {
      get: X3DExecutionContext .prototype .getUnits,
      enumerable: true,
   },
   namedNodes:
   {
      get: X3DExecutionContext .prototype .getNamedNodes,
      enumerable: true,
   },
   importedNodes:
   {
      get: X3DExecutionContext .prototype .getImportedNodes,
      enumerable: true,
   },
   rootNodes:
   {
      get: X3DExecutionContext .prototype .getRootNodes,
      set: X3DExecutionContext .prototype .setRootNodes,
      enumerable: true,
   },
   protos:
   {
      get: X3DExecutionContext .prototype .getProtoDeclarations,
      enumerable: true,
   },
   externprotos:
   {
      get: X3DExecutionContext .prototype .getExternProtoDeclarations,
      enumerable: true,
   },
   routes:
   {
      get: X3DExecutionContext .prototype .getRoutes,
      enumerable: true,
   },
});

Object .defineProperties (X3DExecutionContext,
{
   typeName:
   {
      value: "X3DExecutionContext",
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DExecutionContext .typeName);

const X3DExecutionContext_default_ = X3DExecutionContext;
;

/* harmony default export */ const Execution_X3DExecutionContext = (Namespace/* default */.A .add ("X3DExecutionContext", X3DExecutionContext_default_));
;// ./src/x_ite/Configuration/ComponentInfo.js



function ComponentInfo (name, level, title, providerURL, external = false, dependencies = [ ])
{
   Object .defineProperties (this,
   {
      name: { value: name, enumerable: true },
      level: { value: level, enumerable: true },
      title: { value: title, enumerable: true },
      providerURL: { value: providerURL || Networking_URLs .getProviderURL (external && name), enumerable: true },
      external: { value: external },
      dependencies: { value: dependencies },
   });
}

Object .assign (Object .setPrototypeOf (ComponentInfo .prototype, Base_X3DObject .prototype),
{
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "COMPONENT";
      generator .Space ();
      generator .string += this .name;
      generator .TidySpace ();
      generator .string += ":";
      generator .TidySpace ();
      generator .string += this .level;
   },
   toXMLStream (generator)
   {
      generator .openTag ("component");
      generator .attribute ("name",  this .name);
      generator .attribute ("level", this .level);
      generator .closeTag ("component");
   },
   toJSONStream (generator)
   {
      generator .beginObject ("", false);
      generator .stringProperty ("@name", this .name, false);
      generator .numberProperty ("@level", this .level);
      generator .endObject ();
   },
});

for (const key of Object .keys (ComponentInfo .prototype))
   Object .defineProperty (ComponentInfo .prototype, key, { enumerable: false });

Object .defineProperties (ComponentInfo,
{
   typeName:
   {
      value: "ComponentInfo",
      enumerable: true,
   },
});

Object .defineProperties (ComponentInfo .prototype,
{
   providerUrl: // legacy
   {
      get: function () { return this .providerURL; },
   },
});

const ComponentInfo_default_ = ComponentInfo;
;

/* harmony default export */ const Configuration_ComponentInfo = (Namespace/* default */.A .add ("ComponentInfo", ComponentInfo_default_));
;// ./src/x_ite/Configuration/ComponentInfoArray.js



function ComponentInfoArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .name, value]), Configuration_ComponentInfo);
}

Object .assign (Object .setPrototypeOf (ComponentInfoArray .prototype, Base_X3DInfoArray .prototype),
{
   add (name, { level, title, providerURL, external = false, dependencies = [ ] })
   {
      Base_X3DInfoArray .prototype .add .call (this, name, new Configuration_ComponentInfo (name, level, title, providerURL, external, dependencies));
   },
});

for (const key of Object .keys (ComponentInfoArray .prototype))
   Object .defineProperty (ComponentInfoArray .prototype, key, { enumerable: false });

Object .defineProperties (ComponentInfoArray,
{
   typeName:
   {
      value: "ComponentInfoArray",
      enumerable: true,
   },
});

const ComponentInfoArray_default_ = ComponentInfoArray;
;

/* harmony default export */ const Configuration_ComponentInfoArray = (Namespace/* default */.A .add ("ComponentInfoArray", ComponentInfoArray_default_));
;// ./src/x_ite/Configuration/UnitInfo.js


function UnitInfo (category, name, conversionFactor)
{
   Object .defineProperties (this,
   {
      category: { value: category, enumerable: true },
      name: { value: name, enumerable: true },
      conversionFactor: { value: conversionFactor, enumerable: true },
   });
}

Object .assign (Object .setPrototypeOf (UnitInfo .prototype, Base_X3DObject .prototype),
{
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "UNIT";
      generator .Space ();
      generator .string += this .category;
      generator .Space ();
      generator .string += this .name;
      generator .Space ();
      generator .string += this .conversionFactor;
   },
   toXMLStream (generator)
   {
      generator .openTag ("unit");
      generator .attribute ("category",         this .category);
      generator .attribute ("name",             this .name);
      generator .attribute ("conversionFactor", this .conversionFactor);
      generator .closeTag ("unit");
   },
   toJSONStream (generator, _throw)
   {
      if (this .conversionFactor === 1)
         throw new Error ("conversionFactor is 1");

      generator .beginObject ("", false);
      generator .stringProperty ("@category",         this .category, false);
      generator .stringProperty ("@name",             this .name);
      generator .numberProperty ("@conversionFactor", this .conversionFactor);
      generator .endObject ();
   },
});

for (const key of Object .keys (UnitInfo .prototype))
   Object .defineProperty (UnitInfo .prototype, key, { enumerable: false });

Object .defineProperty (UnitInfo .prototype, "conversion_factor",
{
   get () { return this .conversionFactor; },
});

Object .defineProperties (UnitInfo,
{
   typeName:
   {
      value: "UnitInfo",
      enumerable: true,
   },
});

const UnitInfo_default_ = UnitInfo;
;

/* harmony default export */ const Configuration_UnitInfo = (Namespace/* default */.A .add ("UnitInfo", UnitInfo_default_));
;// ./src/x_ite/Configuration/UnitInfoArray.js



function UnitInfoArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .category, value]), Configuration_UnitInfo);
}

Object .setPrototypeOf (UnitInfoArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (UnitInfoArray .prototype))
   Object .defineProperty (UnitInfoArray .prototype, key, { enumerable: false });

Object .defineProperties (UnitInfoArray,
{
   typeName:
   {
      value: "UnitInfoArray",
      enumerable: true,
   },
});

const UnitInfoArray_default_ = UnitInfoArray;
;

/* harmony default export */ const Configuration_UnitInfoArray = (Namespace/* default */.A .add ("UnitInfoArray", UnitInfoArray_default_));
;// ./src/x_ite/Execution/X3DExportedNode.js



const
   X3DExportedNode_executionContext = Symbol (),
   X3DExportedNode_exportedName     = Symbol (),
   _localNode        = Symbol (),
   X3DExportedNode_description      = Symbol ();

function X3DExportedNode (executionContext, exportedName, localNode, description)
{
   Base_X3DObject .call (this);

   this [X3DExportedNode_executionContext] = executionContext;
   this [X3DExportedNode_exportedName]     = exportedName;
   this [_localNode]        = localNode;
   this [X3DExportedNode_description]      = description;
}

Object .assign (Object .setPrototypeOf (X3DExportedNode .prototype, Base_X3DObject .prototype),
{
   getExecutionContext ()
   {
      return this [X3DExportedNode_executionContext];
   },
   getExportedName ()
   {
      return this [X3DExportedNode_exportedName];
   },
   getLocalNode ()
   {
      return this [_localNode];
   },
   getDescription ()
   {
      return this [X3DExportedNode_description];
   },
   setDescription (value)
   {
      this [X3DExportedNode_description] = String (value);
   },
   toVRMLStream (generator)
   {
      const localName = generator .Name (this .getLocalNode ());

      generator .Indent ();
      generator .string += "EXPORT";
      generator .Space ();
      generator .string += localName;

      if (this [X3DExportedNode_exportedName] !== localName)
      {
         generator .Space ();
         generator .string += "AS";
         generator .Space ();
         generator .string += this [X3DExportedNode_exportedName];
      }

      if (this [X3DExportedNode_description])
      {
         generator .Space ();
         generator .string += "DESCRIPTION";
         generator .Space ();
         generator .string += '"';
         generator .string += this [X3DExportedNode_description];
         generator .string += '"';
      }
   },
   toXMLStream (generator)
   {
      const localName = generator .Name (this .getLocalNode ());

      generator .openTag ("EXPORT");
      generator .attribute ("localDEF", localName);

      if (this [X3DExportedNode_exportedName] !== localName)
         generator .attribute ("AS", this [X3DExportedNode_exportedName]);

      if (this [X3DExportedNode_description])
         generator .attribute ("DESCRIPTION", this [X3DExportedNode_description]);

      generator .closeTag ("EXPORT");
   },
   toJSONStream (generator)
   {
      const localName = generator .Name (this .getLocalNode ());

      generator .TidyBreak ();
      generator .Indent ();

      generator .beginObject ("EXPORT", false, true);
      generator .stringProperty ("@localDEF", localName, false);

      if (this [X3DExportedNode_exportedName] !== localName)
         generator .stringProperty ("@AS", this [X3DExportedNode_exportedName]);

      if (this [X3DExportedNode_description])
         generator .stringProperty ("@DESCRIPTION", this [X3DExportedNode_description]);

      generator .endObject ();
      generator .endObject ();
   },
});

for (const key of Object .keys (X3DExportedNode .prototype))
   Object .defineProperty (X3DExportedNode .prototype, key, { enumerable: false });

Object .defineProperties (X3DExportedNode .prototype,
{
   exportedName:
   {
      get: X3DExportedNode .prototype .getExportedName,
      enumerable: true,
   },
   localNode:
   {
      get ()
      {
         return Fields_SFNodeCache .get (this [_localNode]);
      },
      enumerable: true,
   },
   description:
   {
      get: X3DExportedNode .prototype .getDescription,
      set: X3DExportedNode .prototype .setDescription,
      enumerable: true,
   },
});

Object .defineProperties (X3DExportedNode,
{
   typeName:
   {
      value: "X3DExportedNode",
      enumerable: true,
   },
});

const X3DExportedNode_default_ = X3DExportedNode;
;

/* harmony default export */ const Execution_X3DExportedNode = (Namespace/* default */.A .add ("X3DExportedNode", X3DExportedNode_default_));
;// ./src/x_ite/Execution/ExportedNodesArray.js



function ExportedNodesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .getExportedName (), value]), Execution_X3DExportedNode);
}

Object .setPrototypeOf (ExportedNodesArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (ExportedNodesArray .prototype))
   Object .defineProperty (ExportedNodesArray .prototype, key, { enumerable: false });

Object .defineProperties (ExportedNodesArray,
{
   typeName:
   {
      value: "ExportedNodesArray",
      enumerable: true,
   },
});

const ExportedNodesArray_default_ = ExportedNodesArray;
;

/* harmony default export */ const Execution_ExportedNodesArray = (Namespace/* default */.A .add ("ExportedNodesArray", ExportedNodesArray_default_));
;// ./src/x_ite/LATEST_VERSION.js
// Latest implemented X3D specification version:

const LATEST_VERSION_default_ = "4.1";
;

/* harmony default export */ const LATEST_VERSION = (Namespace/* default */.A .add ("LATEST_VERSION", LATEST_VERSION_default_));
;// ./src/x_ite/Execution/X3DScene.js














const
   _specificationVersion = Symbol (),
   _encoding             = Symbol (),
   _profile              = Symbol (),
   _components           = Symbol (),
   _worldURL             = Symbol (),
   _units                = Symbol (),
   _metadata             = Symbol (),
   X3DScene_exportedNodes        = Symbol (),
   _loadingObjects       = Symbol ();

function X3DScene (browser)
{
   Execution_X3DExecutionContext .call (this, null, null, browser);

   this .addType (Base_X3DConstants .X3DScene)

   this .addChildObjects (Base_X3DConstants .outputOnly, "profile_changed",  new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "metadata_changed", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "loadCount",        new x_ite_Fields .SFInt32 ())

   this .getRootNodes () .setAccessType (Base_X3DConstants .inputOutput);

   this .setLive (false);

   // Private properties

   this [_specificationVersion] = LATEST_VERSION;
   this [_encoding]             = "SCRIPTED";
   this [_profile]              = null;
   this [_components]           = new Configuration_ComponentInfoArray ([ ]);
   this [_worldURL]             = location .toString ();
   this [_units]                = new Configuration_UnitInfoArray ();

   this [_units] .add ("angle",  new Configuration_UnitInfo ("angle",  "radian",   1));
   this [_units] .add ("force",  new Configuration_UnitInfo ("force",  "newton",   1));
   this [_units] .add ("length", new Configuration_UnitInfo ("length", "metre",    1));
   this [_units] .add ("mass",   new Configuration_UnitInfo ("mass",   "kilogram", 1));

   this [_metadata]       = [ ];
   this [X3DScene_exportedNodes]  = new Execution_ExportedNodesArray ();
   this [_loadingObjects] = new Set ();

   this [_components]    .addParent (this);
   this [_units]         .addParent (this);
   this [X3DScene_exportedNodes] .addParent (this);
}

Object .assign (Object .setPrototypeOf (X3DScene .prototype, Execution_X3DExecutionContext .prototype),
{
   getLocalScene ()
   {
      return this;
   },
   setSpecificationVersion (specificationVersion)
   {
      this [_specificationVersion] = String (specificationVersion);
   },
   getSpecificationVersion ()
   {
      return this [_specificationVersion];
   },
   setEncoding (encoding)
   {
      this [_encoding] = String (encoding);
   },
   getEncoding ()
   {
      return this [_encoding];
   },
   setWorldURL (url)
   {
      this [_worldURL] = String (url);
   },
   getWorldURL ()
   {
      return this [_worldURL];
   },
   getBaseURL ()
   {
      if (this [_worldURL] .match (/^(?:data|blob):/))
      {
         return this .getExecutionContext () ?.getBaseURL ()
            ?? this .getBrowser () .getBaseURL ();
      }

      return this [_worldURL];
   },
   setProfile (profile)
   {
      this [_profile] = profile;

      this ._profile_changed = Date .now () / 1000;
   },
   getProfile ()
   {
      return this [_profile];
   },
   hasComponent (name, level = 0)
   {
      if (name instanceof Configuration_ComponentInfo)
         var { name, level } = name;

      const
         browser = this .getBrowser (),
         profile = this [_profile] ?? browser .getProfile ("Full");

      return [profile .components .get (name), this [_components] .get (name)]
         .some (component => component && level <= component .level);
   },
   addComponent (component)
   {
      if (this [_components] .get (component .name))
         throw new Error (`Couldn't add component. Component '${component .name}' already exists.`);

      this [_components] .add (component .name, component);

      this ._components_changed = Date .now () / 1000;
   },
   updateComponent (component)
   {
      this [_components] .update (component .name, component .name, component);

      this ._components_changed = Date .now () / 1000;
   },
   removeComponent (name)
   {
      this [_components] .remove (name);

      this ._components_changed = Date .now () / 1000;
   },
   getComponents ()
   {
      return this [_components];
   },
   updateUnit (category, name, conversionFactor)
   {
      // Private function.

      if (!this [_units] .has (category))
         return;

      this [_units] .update (category, category, new Configuration_UnitInfo (category, String (name), +conversionFactor));

      this ._units_changed = Date .now () / 1000;
   },
   getUnit (category)
   {
      return this [_units] .get (category);
   },
   getUnits ()
   {
      return this [_units];
   },
   fromUnit (category, value)
   {
      switch (category)
      {
         // Base units

         case "angle":
         case "force":
         case "length":
         case "mass":
            return value * this .getUnits () .get (category) .conversionFactor;

         // Derived units

         case "acceleration:":
            return value * this .getUnits () .get ("length") .conversionFactor;
         case "angularRate":
            return value * this .getUnits () .get ("angle") .conversionFactor;
         case "area":
            return value * this .getUnits () .get ("length") .conversionFactor ** 2;
         case "speed":
            return value * this .getUnits () .get ("length") .conversionFactor;
         case "volume":
            return value * this .getUnits () .get ("length") .conversionFactor ** 3;
      }

      return value;
   },
   toUnit (category, value)
   {
      switch (category)
      {
         // Base units

         case "angle":
         case "force":
         case "length":
         case "mass":
            return value / this .getUnits () .get (category) .conversionFactor;

         // Derived units

         case "acceleration:":
            return value / this .getUnits () .get ("length") .conversionFactor;
         case "angularRate":
            return value / this .getUnits () .get ("angle") .conversionFactor;
         case "area":
            return value / this .getUnits () .get ("length") .conversionFactor ** 2;
         case "speed":
            return value / this .getUnits () .get ("length") .conversionFactor;
         case "volume":
            return value / this .getUnits () .get ("length") .conversionFactor ** 3;
      }

      return value;
   },
   setMetaData (name, values)
   {
      name   = String (name);
      values = Array .isArray (values) ? values .map (String) : [String (values)];

      if (!name .length)
         throw new Error ("Couldn't add metadata: name is empty.");

      let index = this [_metadata] .findIndex (entry => entry [0] === name);

      this .removeMetaData (name);

      if (index === -1)
         index = this [_metadata] .length;

      for (const [i, value] of values .entries ())
         this [_metadata] .splice (index + i, 0, [name, value]);

      this ._metadata_changed = Date .now () / 1000;
   },
   addMetaData (name, values)
   {
      name   = String (name);
      values = Array .isArray (values) ? values .map (String) : [String (values)];

      if (!name .length)
         throw new Error ("Couldn't add metadata: name is empty.");

      for (const value of values)
         this [_metadata] .push ([name, value]);

      this ._metadata_changed = Date .now () / 1000;
   },
   removeMetaData (name)
   {
      name = String (name);

      this [_metadata] = this [_metadata] .filter (entry => entry [0] !== name);

      this ._metadata_changed = Date .now () / 1000;
   },
   getMetaData (name)
   {
      name = String (name);

      const values = this [_metadata]
         .filter (entry => entry [0] === name)
         .map (entry => entry [1]);

      if (values .length)
         return values;

      return undefined;
   },
   getMetaDatas ()
   {
      return this [_metadata] .map (entry => entry .slice ());
   },
   addExportedNode (exportedName, node, description = "")
   {
      exportedName = String (exportedName);

      if (this [X3DScene_exportedNodes] .has (exportedName))
         throw new Error (`Couldn't add exported node: exported name '${exportedName}' already in use.`);

      this .updateExportedNode (exportedName, node, description);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   updateExportedNode (exportedName, node, description = "")
   {
      exportedName = String (exportedName);
      node         = Base_X3DCast (Base_X3DConstants .X3DNode, node, false);
      description  = String (description);

      if (exportedName .length === 0)
         throw new Error ("Couldn't update exported node: node exported name is empty.");

      if (!node)
         throw new Error ("Couldn't update exported node: node must be of type X3DNode.");

      //if (node .getExecutionContext () !== this)
      //   throw new Error ("Couldn't update exported node: node does not belong to this execution context.");

      const exportedNode = new Execution_X3DExportedNode (this, exportedName, node, description);

      this [X3DScene_exportedNodes] .update (exportedName, exportedName, exportedNode);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   removeExportedNode (exportedName)
   {
      exportedName = String (exportedName);

      this [X3DScene_exportedNodes] .remove (exportedName);

      this ._sceneGraph_changed = Date .now () / 1000;
   },
   getExportedNode (exportedName)
   {
      exportedName = String (exportedName);

      const exportedNode = this [X3DScene_exportedNodes] .get (exportedName);

      if (exportedNode)
         return exportedNode .localNode;

      throw new Error (`Exported node '${exportedName}' not found.`);
   },
   getExportedNodes ()
   {
      return this [X3DScene_exportedNodes];
   },
   getUniqueExportName (name)
   {
      return getUniqueName (this [X3DScene_exportedNodes], name);
   },
   addRootNode (node)
   {
      node = Fields_SFNodeCache .get (Base_X3DCast (Base_X3DConstants .X3DNode, node, false));

      const rootNodes = this .getRootNodes ();

      if (rootNodes .includes (node))
         return;

      rootNodes .push (node);
   },
   removeRootNode (node)
   {
      node = Fields_SFNodeCache .get (Base_X3DCast (Base_X3DConstants .X3DNode, node, false));

      const rootNodes = this .getRootNodes ();

      rootNodes .assign (rootNodes .filter (rootNode => rootNode !== node));
   },
   setRootNodes (value)
   {
      this .getRootNodes () .assign (value);
   },
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "#X3D V";
      generator .string += LATEST_VERSION;
      generator .Space ();
      generator .string += "utf8";
      generator .Space ();
      generator .string += this .getBrowser () .name;
      generator .Space ();
      generator .string += "V";
      generator .string += this .getBrowser () .version;
      generator .string += "\n";
      generator .string += "\n";

      const profile = this .getProfile ();

      if (profile)
      {
         profile .toVRMLStream (generator);

         generator .Break ();
         generator .TidyBreak ();
      }

      const components = this .getComponents ();

      if (components .length)
      {
         components .toVRMLStream (generator);

         generator .TidyBreak ();
      }

      const units = this .getUnits () .filter (unit => unit .conversionFactor !== 1);

      if (units .length)
      {
         for (const unit of units)
         {
            unit .toVRMLStream (generator);

            generator .Break ();
         }

         generator .TidyBreak ();
      }

      const metadata = this .getMetaDatas ();

      if (metadata .length)
      {
         for (const [key, value] of metadata)
         {
            generator .Indent ();
            generator .string += "META";
            generator .Space ();
            generator .string += new x_ite_Fields .SFString (key) .toString ();
            generator .Space ();
            generator .string += new x_ite_Fields .SFString (value) .toString ();
            generator .Break ();
         }

         generator .TidyBreak ();
      }

      const exportedNodes = this .getExportedNodes ();

      generator .PushExecutionContext (this);
      generator .ExportedNodes (exportedNodes);

      Execution_X3DExecutionContext .prototype .toVRMLStream .call (this, generator);

      if (exportedNodes .length)
      {
         generator .TidyBreak ();

         exportedNodes .toVRMLStream (generator);
      }

      generator .PopExecutionContext ();
   },
   toXMLStream (generator)
   {
      if (!generator .html)
      {
         generator .Indent ();
         generator .string += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
         generator .TidyBreak ();
         generator .Indent ();
         generator .string += "<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D ";
         generator .string += LATEST_VERSION;
         generator .string += "//EN\" \"https://www.web3d.org/specifications/x3d-";
         generator .string += LATEST_VERSION;
         generator .string += ".dtd\">";
         generator .TidyBreak ();
      }

      generator .openTag ("X3D");
      generator .attribute ("profile",   this .getProfile () ?.name ?? "Full");
      generator .attribute ("version",   LATEST_VERSION);
      generator .attribute ("xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance");
      generator .attribute ("xsd:noNamespaceSchemaLocation", `https://www.web3d.org/specifications/x3d-${LATEST_VERSION}.xsd`);

      generator .endTag ();
      generator .IncIndent ();

      const metadata = this .getMetaDatas ();

      if (this .getComponents () .length ||
          this .getUnits () .some (unit => unit .conversionFactor !== 1) ||
          metadata .length)
      {
         generator .openingTag ("head");
         generator .TidyBreak ();
         generator .IncIndent ();

         // <head>

         this .getComponents () .toXMLStream (generator);

         for (const unit of this .getUnits ())
         {
            if (unit .conversionFactor !== 1)
            {
               unit .toXMLStream (generator);
               generator .TidyBreak ();
            }
         }

         for (const [key, value] of metadata)
         {
            generator .openTag ("meta");
            generator .attribute ("name",    key);
            generator .attribute ("content", value);
            generator .closeTag ("meta");
            generator .TidyBreak ();
         }

         // </head>

         generator .DecIndent ();
         generator .closingTag ("head");
         generator .TidyBreak ();
      }

      if (this .getExternProtoDeclarations () .length ||
          this .getProtoDeclarations () .length ||
          this .getRootNodes () .length)
      {
         generator .openingTag ("Scene");
         generator .TidyBreak ();
         generator .IncIndent ();

         // <Scene>

         const exportedNodes = this .getExportedNodes ();

         generator .PushExecutionContext (this);
         generator .ExportedNodes (exportedNodes);

         Execution_X3DExecutionContext .prototype .toXMLStream .call (this, generator);

         exportedNodes .toXMLStream (generator);

         generator .PopExecutionContext ();
         generator .DecIndent ();
         generator .closingTag ("Scene");
      }
      else
      {
         generator .openTag ("Scene");
         generator .closeTag ("Scene");
      }

      generator .TidyBreak ();
      generator .DecIndent ();
      generator .closingTag ("X3D");
      generator .TidyBreak ();
   },
   toJSONStream (generator)
   {
      // X3D

      generator .beginObject ("X3D", false, true);
      generator .stringProperty ("encoding", "UTF-8", false);
      generator .stringProperty ("@profile", this .getProfile () ?.name ?? "Full");
      generator .stringProperty ("@version", LATEST_VERSION);
      generator .stringProperty ("@xsd:noNamespaceSchemaLocation", `https://www.web3d.org/specifications/x3d-${LATEST_VERSION}.xsd`);
      generator .stringProperty ("JSON schema", `https://www.web3d.org/specifications/x3d-${LATEST_VERSION}-JSONSchema.json`);

      // Head

      const
         outputUnits = this .getUnits () .some (unit => unit .conversionFactor !== 1),
         metadata    = this .getMetaDatas ();

      if (this .getComponents () .length || outputUnits || metadata .length)
      {
         let headLastProperty = false;

         generator .beginObject ("head");

         // Meta data

         if (metadata .length)
         {
            // Meta data begin

            generator .beginArray ("meta", headLastProperty);

            // Meta data

            for (const [i, [key, value]] of metadata .entries ())
            {
               generator .beginObject ("", i !== 0);
               generator .stringProperty ("@name",    key, false);
               generator .stringProperty ("@content", value);
               generator .endObject ();
            }

            // Meta data end

            generator .endArray ();

            headLastProperty = true;
         }

         // Components

         if (this .getComponents () .length)
         {
            generator .beginArray ("component", headLastProperty);

            this .getComponents () .toJSONStream (generator);

            generator .endArray ();

            headLastProperty = true;
         }

         // Units

         if (outputUnits)
         {
            generator .beginArray ("unit", headLastProperty);

            this .getUnits () .toJSONStream (generator);

            generator .endArray ();

            headLastProperty = true;
         }

         // Head end

         generator .endObject ();
      }

      // Scene

      generator .beginObject ("Scene");
      generator .beginArray ("-children", false);

      const exportedNodes = this .getExportedNodes ();

      generator .PushExecutionContext (this);
      generator .ExportedNodes (exportedNodes);

      const comma = Execution_X3DExecutionContext .prototype .toJSONStream .call (this, generator);

      // Exported nodes

      this .getExportedNodes () .toJSONStream (generator, comma);

      generator .PopExecutionContext ();

      // Scene end

      generator .endArray ();
      generator .endObject ();

      // X3D end

      generator .endObject ();
      generator .endObject ();
      generator .TidyBreak ();
   },
   dispose ()
   {
      this .exportedNodes .clear ();

      Execution_X3DExecutionContext .prototype .dispose .call (this);
   },
},
{
   setExecutionContext (executionContext)
   {
      // Remove loading objects from old parent scene.
      {
         const scene = this .getScene ();

         if (scene)
         {
            for (const object of this [_loadingObjects])
               scene .removeLoadingObject (object);
         }
      }

      Execution_X3DExecutionContext .prototype .setExecutionContext .call (this, executionContext);

      // Add loading objects to new parent scene.
      {
         const scene = this .getScene ();

         if (scene)
         {
            for (const object of this [_loadingObjects])
               scene .addLoadingObject (object);
         }
      }
   },
   getLoadingObjects ()
   {
      return this [_loadingObjects];
   },
   addLoadingObject (node)
   {
      this [_loadingObjects] .add (node);

      this ._loadCount = this [_loadingObjects] .size;

      const parent = this .getScene () ?? this .getBrowser ();

      parent .addLoadingObject (node);
   },
   removeLoadingObject (node)
   {
      this [_loadingObjects] .delete (node);

      this ._loadCount = this [_loadingObjects] .size;

      const parent = this .getScene () ?? this .getBrowser ();

      parent .removeLoadingObject (node);
   },
});

for (const key of Object .keys (X3DScene .prototype))
   Object .defineProperty (X3DScene .prototype, key, { enumerable: false });

Object .defineProperties (X3DScene .prototype,
{
   isScene:
   {
      value: true,
      enumerable: true,
   },
   specificationVersion:
   {
      get: X3DScene .prototype .getSpecificationVersion,
      enumerable: true,
   },
   encoding:
   {
      get: X3DScene .prototype .getEncoding,
      enumerable: true,
   },
   profile:
   {
      get: X3DScene .prototype .getProfile,
      enumerable: true,
   },
   profile_changed:
   {
      get () { return this ._profile_changed; },
      enumerable: false,
   },
   components:
   {
      get: X3DScene .prototype .getComponents,
      enumerable: true,
   },
   worldURL:
   {
      get: X3DScene .prototype .getWorldURL,
      enumerable: true,
   },
   baseURL:
   {
      get: X3DScene .prototype .getBaseURL,
      enumerable: true,
   },
   units:
   {
      get: X3DScene .prototype .getUnits,
      enumerable: true,
   },
   metadata_changed:
   {
      get () { return this ._metadata_changed; },
      enumerable: false,
   },
   rootNodes:
   {
      get: X3DScene .prototype .getRootNodes,
      set: X3DScene .prototype .setRootNodes,
      enumerable: true,
   },
   exportedNodes:
   {
      get: X3DScene .prototype .getExportedNodes,
      enumerable: true,
   },
   sceneGraph_changed:
   {
      // Fires when nodes are added or removed.
      get () { return this ._sceneGraph_changed; },
      enumerable: false,
   },
   bbox_changed:
   {
      // Fires when somewhere in this scene a node changes its bbox.
      get () { return this ._bbox_changed; },
      enumerable: false,
   },
});

Object .defineProperties (X3DScene,
{
   typeName:
   {
      value: "X3DScene",
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DScene .typeName);

const X3DScene_default_ = X3DScene;
;

/* harmony default export */ const Execution_X3DScene = (Namespace/* default */.A .add ("X3DScene", X3DScene_default_));
;// ./src/standard/Utility/DataStorage.js
const
   storages   = new WeakMap (),
   namespaces = new WeakMap (),
   defaults   = new WeakMap ();

const DataStorage_handler =
{
   get (target, key)
   {
      const property = target [key];

      if (property !== undefined)
         return property;

      const value = target .getStorage () [target .getNameSpace () + key];

      if (String (value) .match (/^(?:undefined|null)$/))
         return target .getDefaultValue (key);

      return JSON .parse (value);
   },
   set (target, key, value)
   {
      if (value === undefined)
         target .getStorage () .removeItem (target .getNameSpace () + key);

      else
         target .getStorage () [target .getNameSpace () + key] = JSON .stringify (value);

      return true;
   },
};

function DataStorage (storage, namespace)
{
   this .target  = this;

   storages   .set (this, storage);
   namespaces .set (this, namespace);
   defaults   .set (this, { });

   return new Proxy (this, DataStorage_handler);
}

Object .assign (DataStorage .prototype,
{
   getStorage ()
   {
      return storages .get (this .target);
   },
   getNameSpace ()
   {
      return namespaces .get (this .target);
   },
   addNameSpace (namespace)
   {
      return new DataStorage (this .getStorage (), this .getNameSpace () + namespace);
   },
   setDefaultValues (object)
   {
      Object .assign (defaults .get (this .target), object);
   },
   getDefaultValue (key)
   {
      const value = defaults .get (this .target) [key];

      return value === undefined ? undefined : JSON .parse (JSON .stringify (value));
   },
   clear ()
   {
      const
         storage   = this .getStorage (),
         namespace = this .getNameSpace ();

      for (const key of Object .keys (storage))
      {
         if (key .startsWith (namespace))
            storage .removeItem (key)
      }
   },
});

const DataStorage_default_ = DataStorage;
;

/* harmony default export */ const Utility_DataStorage = (Namespace/* default */.A .add ("DataStorage", DataStorage_default_));
;// ./src/x_ite/Browser/Core/Fonts.js


function add (path, bold, italic)
{
   try
   {
      // In case of people embed the CSS file in a HTML page, we need to check if the
      // font is already loaded. If so, we should not add it again.

      document .fonts .add (new FontFace ("X_ITE PT Sans", `url(${Networking_URLs .getFontsURL (path)})`,
      {
         style: italic ? "italic" : "normal",
         weight: bold ? "700" : "400",
         display: "block",
      }));
   }
   catch (error)
   {
      console .error (error);
   }
}

add ("PT_Sans/PTSans-Regular.woff2",    false, false);
add ("PT_Sans/PTSans-Bold.woff2",       true,  false);
add ("PT_Sans/PTSans-Italic.woff2",     false, true);
// add ("PT_Sans/PTSans-BoldItalic.woff2", true,  true);

;// ./src/x_ite/Browser/Core/X3DCoreContext.js
/* provided dependency */ var X3DCoreContext_$ = __webpack_require__(44).A;



















const
   _instanceId          = Symbol (),
   _element             = Symbol (),
   _shadow              = Symbol (),
   _surface             = Symbol (),
   _canvas              = Symbol (),
   _context             = Symbol (),
   _splashScreen        = Symbol (),
   _localStorage        = Symbol (),
   _browserTimings      = Symbol (),
   _browserOptions      = Symbol (),
   _browserProperties   = Symbol (),
   _renderingProperties = Symbol (),
   _notification        = Symbol (),
   _contextMenu         = Symbol (),
   _privateScene        = Symbol (),
   _keydown             = Symbol (),
   _keyup               = Symbol ();

let instanceId = 0;

function X3DCoreContext (element)
{
   // Events

   this .addChildObjects (Base_X3DConstants .outputOnly, "controlKey", new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "shiftKey",   new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "altKey",     new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "commandKey", new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "altGrKey",   new x_ite_Fields .SFBool ());

   // Get canvas & context.

   const
      browser      = X3DCoreContext_$("<div></div>", { class: "x_ite-private-browser", part: "browser", tabindex: 0 }),
      surface      = X3DCoreContext_$("<div></div>", { class: "x_ite-private-surface", part: "surface" }),
      splashScreen = X3DCoreContext_$("<div></div>", { class: "x_ite-private-splash-screen x_ite-private-hidden" }),
      spinner      = X3DCoreContext_$("<div></div>", { class: "x_ite-private-spinner" }),
      progress     = X3DCoreContext_$("<div></div>", { class: "x_ite-private-progress" }),
      buttons      = X3DCoreContext_$("<div></div>", { class: "x_ite-private-buttons", part: "buttons" });

   surface      .appendTo (browser);
   splashScreen .appendTo (browser);
   spinner      .appendTo (splashScreen);
   progress     .appendTo (splashScreen);
   buttons      .appendTo (surface);

   if (element .prop ("nodeName") .toLowerCase () === "x3d-canvas")
   {
      const shadow = X3DCoreContext_$(element [0] .attachShadow ({ mode: "open", delegatesFocus: true }));

      const stylesheet = new Promise (resolve =>
      {
         X3DCoreContext_$("<link/>",
         {
            on: { load: resolve },
            integrity: "integrity-x_ite-css",
            crossorigin: "anonymous",
            rel: "stylesheet",
            href: new URL ("x_ite.css", Networking_URLs .getScriptURL ()),
         })
         .appendTo (shadow);
      });

      this [_shadow] = shadow .append (browser .hide ());

      stylesheet .then (() => browser .show ());
   }
   else
   {
      this [_shadow] = element .prepend (browser);
   }

   X3DCoreContext_$("<div></div>", { class: "x_ite-private-x_ite" }) .html (`${this .getName ()}<b>X3D</b>`) .appendTo (progress);
   X3DCoreContext_$("<div></div>", { class: "x_ite-private-progressbar" })  .appendTo (progress) .append (X3DCoreContext_$("<div></div>"));
   X3DCoreContext_$("<div></div>", { class: "x_ite-private-spinner-text" }) .appendTo (progress);

   this [_instanceId]   = ++ instanceId;
   this [_localStorage] = new Utility_DataStorage (localStorage, `X_ITE.X3DBrowser(${this [_instanceId]}).`);
   this [_element]      = element;
   this [_surface]      = surface;
   this [_canvas]       = X3DCoreContext_$("<canvas></canvas>", { part: "canvas", class: "x_ite-private-canvas" }) .prependTo (surface);
   this [_context]      = Core_Context .create (this [_canvas] [0], element .attr ("preserveDrawingBuffer") === "true");
   this [_splashScreen] = splashScreen;

   this [_renderingProperties] = new Core_RenderingProperties (this .getPrivateScene ());
   this [_browserOptions]      = new Core_BrowserOptions      (this .getPrivateScene ());
   this [_browserProperties]   = new Core_BrowserProperties   (this .getPrivateScene ());
   this [_browserTimings]      = new Core_BrowserTimings      (this .getPrivateScene ());
   this [_notification]        = new Core_Notification        (this .getPrivateScene ());
   this [_contextMenu]         = new Core_ContextMenu         (this .getPrivateScene ());
}

Object .assign (X3DCoreContext .prototype,
{
   initialize ()
   {
      const element = this .getElement ();

      // Setup browser nodes.

      this [_renderingProperties] .setup ();
      this [_browserOptions]      .setup ();
      this [_browserProperties]   .setup ();
      this [_browserTimings]      .setup ();
      this [_notification]        .setup ();
      this [_contextMenu]         .setup ();

      // Define properties of X3DCanvasElement.

      Object .defineProperties (element [0],
      {
         browser:
         {
            value: this,
            enumerable: true,
         },
         ... Browser_Legacy .properties (this,
         {
            src:
            {
               get: () =>
               {
                  return element .attr ("src");
               },
               set: (value) =>
               {
                  element .attr ("src", value);
               },
               enumerable: true,
            },
            url:
            {
               get: () =>
               {
                  return this .parseUrlAttribute (element .attr ("url"));
               },
               set: (value) =>
               {
                  element .attr ("url", [... value] .map (src => `"${src}"`) .join (", "));
               },
               enumerable: true,
            },
         }),
      });

      // Configure browser event handlers.

      element
         .on ("keydown.X3DCoreContext", this [_keydown] .bind (this))
         .on ("keyup.X3DCoreContext",   this [_keyup]   .bind (this));

      // Workaround for a bug in Chrome (v135) where attributeChangedCallback is not
      // initially called for attributes set in XHTML.

      (() =>
      {
         if (element .prop ("nodeName") .toUpperCase () !== "X3D-CANVAS")
            return;

         if (document .contentType !== "application/xhtml+xml")
            return;

         if (!navigator .userAgent .includes ("Chrome/"))
            return;

         setTimeout (() =>
         {
            for (const { name, value } of element [0] .attributes)
               this .attributeChangedCallback (name, undefined, value);
         });
      })();
   },
   getInstanceId ()
   {
      return this [_instanceId];
   },
   getElement ()
   {
      return this [_element];
   },
   getShadow ()
   {
      return this [_shadow];
   },
   getSurface ()
   {
      return this [_surface];
   },
   getSplashScreen ()
   {
      return this [_splashScreen];
   },
   getCanvas ()
   {
      return this [_canvas];
   },
   getContext ()
   {
      return this [_context];
   },
   getLocalStorage ()
   {
      return this [_localStorage];
   },
   getBrowserTimings ()
   {
      return this [_browserTimings];
   },
   getBrowserOptions ()
   {
      return this [_browserOptions];
   },
   getBrowserProperties ()
   {
      return this [_browserProperties];
   },
   getRenderingProperties ()
   {
      return this [_renderingProperties];
   },
   getNotification ()
   {
      return this [_notification];
   },
   getContextMenu ()
   {
      return this [_contextMenu];
   },
   getPrivateScene ()
   {
      // X3DScene for default nodes.

      return this [_privateScene] ??= (() =>
      {
         const privateScene = new Execution_X3DScene (this);

         privateScene .checkLiveState = () => true;
         privateScene .setLive (true);
         privateScene .setup ();

         return privateScene;
      })();
   },
   connectedCallback ()
   {
      // AutoUpdate

      this .getBrowserOptions () .checkUpdate ();
   },
   disconnectedCallback ()
   {
      // AutoUpdate

      this .getBrowserOptions () .checkUpdate ();
   },
   attributeChangedCallback (name, oldValue, newValue)
   {
      switch (name .toLowerCase ())
      {
         case "antialiased":
         {
            this .setBrowserOption ("Antialiased", this .parseBooleanAttribute (newValue) ?? true);
            break;
         }
         case "baseurl":
         {
            this .setBaseURL (newValue);
            break;
         }
         case "cache":
         {
            this .setBrowserOption ("Cache", this .parseBooleanAttribute (newValue) ?? true);
            break;
         }
         case "colorspace":
         {
            this .setBrowserOption ("ColorSpace", newValue);
            break;
         }
         case "contentscale":
         {
            this .setBrowserOption ("ContentScale", newValue === "auto" ? -1 : newValue);
            break;
         }
         case "contextmenu":
         {
            this .setBrowserOption ("ContextMenu", this .parseBooleanAttribute (newValue) ?? true);
            break;
         }
         case "debug":
         {
            this .setBrowserOption ("Debug", this .parseBooleanAttribute (newValue) ?? false);
            break;
         }
         case "displaycolorspace":
         {
            this .setBrowserOption ("DisplayColorSpace", newValue);
            break;
         }
         case "exposure":
         {
            this .setBrowserOption ("Exposure", newValue);
            break;
         }
         case "logarithmicdepthbuffer":
         {
            this .setBrowserOption ("LogarithmicDepthBuffer", this .parseBooleanAttribute (newValue) ?? false);
            break;
         }
         case "maximumframerate":
         {
            this .setBrowserOption ("MaximumFrameRate", newValue);
            break;
         }
         case "multisampling":
         {
            this .setBrowserOption ("Multisampling", samples);
            break;
         }
         case "notifications":
         {
            this .setBrowserOption ("Notifications", this .parseBooleanAttribute (newValue) ?? true);
            break;
         }
         case "oninitialized":
         case "onshutdown":
         {
            try
            {
               this .getElement () [0] [name] = new Function ("event", newValue);
            }
            catch (error)
            {
               console .error (error);
            }

            break;
         }
         case "orderindependenttransparency":
         {
            this .setBrowserOption ("OrderIndependentTransparency", this .parseBooleanAttribute (newValue) ?? false);
            break;
         }
         case "splashscreen":
         {
            this .setBrowserOption ("SplashScreen", this .parseBooleanAttribute (newValue) ?? true);

            if (!this .getBrowserOption ("SplashScreen"))
            {
               this .getCanvas () .show ();

               this .getSplashScreen ()
                  .removeClass ("x_ite-private-fade-out-splash-screen")
                  .addClass ("x_ite-private-hidden");
            }

            break;
         }
         case "src":
         {
            if (newValue)
            {
               this .loadURL (new x_ite_Fields .MFString (newValue))
                  .catch (error => console .error (error));
            }

            break;
         }
         case "textcompression":
         {
            this .setBrowserOption ("TextCompression", newValue || "CHAR_SPACINGS");
            break;
         }
         case "timings":
         {
            this .setBrowserOption ("Timings", this .parseBooleanAttribute (newValue) ?? false);
            break;
         }
         case "tonemapping":
         {
            this .setBrowserOption ("ToneMapping", newValue || "NONE");
            break;
         }
         case "update":
         {
            if (newValue === "auto")
            {
               this .setBrowserOption ("AutoUpdate", true);
            }
            else
            {
               this .setBrowserOption ("AutoUpdate", false);

               if (this .parseBooleanAttribute (newValue) ?? true)
                  this .beginUpdate ();
               else
                  this .endUpdate ();
            }

            break;
         }
         case "url":
         {
            if (newValue)
            {
               this .loadURL (this .parseUrlAttribute (newValue))
                  .catch (error => console .error (error));
            }

            break;
         }
         case "xrsessionmode":
         {
            this .setBrowserOption ("XRSessionMode", newValue || "IMMERSIVE_VR");
            break;
         }
      }
   },
   parseBooleanAttribute (value)
   {
      if (value === "true")
         return true;

      if (value === "false")
         return false;
   },
   parseUrlAttribute (urlCharacters)
   {
      try
      {
         const url = new x_ite_Fields .MFString ();

         url .setName ("url");
         url .fromXMLString (urlCharacters, this .getExecutionContext ());

         return url;
      }
      catch (error)
      {
         throw new Error ("Couldn't parse url attribute.");
      }
   },
   callBrowserEventHandler: (() =>
   {
      const build_in = new Set (["error", "load"]);

      return function (events)
      {
         const element = this .getElement () [0];

         for (const name of events .split (" "))
         {
            // Order is attribute, then dispatch.

            const event = new CustomEvent (name);

            try
            {
               if (!build_in .has (name))
                  element [`on${name}`] ?.(event);
            }
            catch (error)
            {
               console .error (error);
            }

            element .dispatchEvent (event);
         }
      };
   })(),
   getShiftKey ()
   {
      return this ._shiftKey .getValue ();
   },
   getControlKey ()
   {
      return this ._controlKey .getValue ();
   },
   getAltKey ()
   {
      return this ._altKey .getValue ();
   },
   getCommandKey ()
   {
      return this ._commandKey .getValue ();
   },
   getAltGrKey ()
   {
      return this ._altGrKey .getValue ();
   },
   [_keydown] (event)
   {
      // console .log (event .keyCode);

      switch (event .keyCode)
      {
         case 16: // Shift
         {
            this ._shiftKey = true;
            break;
         }
         case 17: // Ctrl
         {
            this ._controlKey = true;
            break;
         }
         case 18: // Alt
         {
            this ._altKey = true;
            break;
         }
         case 49: // 1
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();
                  this .setBrowserOption ("Shading", "POINT");
                  this .setDescription ("Shading: Pointset");
               }
            }

            break;
         }
         case 50: // 2
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();
                  this .setBrowserOption ("Shading", "WIREFRAME");
                  this .setDescription ("Shading: Wireframe");
               }
            }

            break;
         }
         case 51: // 3
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();
                  this .setBrowserOption ("Shading", "FLAT");
                  this .setDescription ("Shading: Flat");
               }
            }

            break;
         }
         case 52: // 4
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();
                  this .setBrowserOption ("Shading", "GOURAUD");
                  this .setDescription ("Shading: Gouraud");
               }
            }

            break;
         }
         case 53: // 5
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();
                  this .setBrowserOption ("Shading", "PHONG");
                  this .setDescription ("Shading: Phong");
               }
            }

            break;
         }
         case 83: // s
         {
            if (this .getBrowserOption ("Debug"))
            {
               if (this .getControlKey ())
               {
                  event .preventDefault ();

                  if (this .isLive ())
                     this .endUpdate ();
                  else
                     this .beginUpdate ();

                  this .setDescription (this .isLive () ? "Begin Update" : "End Update");
               }
            }

            break;
         }
         case 91: // Command
         {
            this ._commandKey = true;
            break;
         }
         case 225: // Alt Gr
         {
            this ._altGrKey = true;
            break;
         }
         case 171: // Plus // Firefox
         case 187: // Plus // Opera
         {
            if (this .getControlKey ())
            {
               event .preventDefault ();
               this .setBrowserOption ("Timings", !this .getBrowserOption ("Timings"));
            }

            break;
         }
         case 36: // Pos 1
         {
            event .preventDefault ();
            this .firstViewpoint ();
            break;
         }
         case 35: // End
         {
            event .preventDefault ();
            this .lastViewpoint ();
            break;
         }
         case 33: // Page Up
         {
            event .preventDefault ();
            this .previousViewpoint ();
            break;
         }
         case 34: // Page Down
         {
            event .preventDefault ();
            this .nextViewpoint ();
            break;
         }
         case 119: // F8
         {
            if (this .getShiftKey ())
            {
               event .preventDefault ();

               // Create viewpoint node.

               const viewpoint = this .getActiveViewpoint ();

               if (!viewpoint)
                  break;

               const
                  executionContext = this .getExecutionContext (),
                  vp               = executionContext .createNode (viewpoint .getTypeName ());

               switch (viewpoint .getTypeName ())
               {
                  case "Viewpoint":
                  {
                     vp .position         = viewpoint .getUserPosition ();
                     vp .orientation      = viewpoint .getUserOrientation ();
                     vp .centerOfRotation = viewpoint .getUserCenterOfRotation ();
                     vp .fieldOfView      = viewpoint .getUserFieldOfView ();
                     break;
                  }
                  case "OrthoViewpoint":
                  {
                     vp .position         = viewpoint .getUserPosition ();
                     vp .orientation      = viewpoint .getUserOrientation ();
                     vp .centerOfRotation = viewpoint .getUserCenterOfRotation ();
                     vp .fieldOfView      = viewpoint .getUserFieldOfView ();
                     break;
                  }
                  case "GeoViewpoint":
                  {
                     const
                        geoOrigin = viewpoint ._geoOrigin,
                        geoCoord  = new Numbers_Vector3 ();

                     if (geoOrigin .getValue () && geoOrigin .getNodeTypeName () === "GeoOrigin")
                     {
                        const go = executionContext .createNode ("GeoOrigin");

                        vp .geoOrigin = go;
                        go .geoSystem = geoOrigin .geoSystem;
                        go .geoCoords = geoOrigin .geoCoords;
                        go .rotateYUp = geoOrigin .rotateYUp;
                     }

                     vp .geoSystem        = viewpoint ._geoSystem;
                     vp .position         = viewpoint .getGeoCoord (viewpoint .getUserPosition (), geoCoord);
                     vp .orientation      = viewpoint .getUserOrientation ();
                     vp .centerOfRotation = viewpoint .getGeoCoord (viewpoint .getUserCenterOfRotation (), geoCoord);
                     vp .fieldOfView      = viewpoint .getUserFieldOfView ();
                     break;
                  }
               }

               // Create text.

               const options = x_ite_Features .ENVIRONMENT === "NODE" ? { } : { scene: executionContext };

               let text;

               switch (executionContext .getEncoding ())
               {
                  case "ASCII":
                  case "VRML": text = vp .toVRMLString (options); break;
                  case "JSON": text = vp .toJSONString (options); break;
                  default:     text = vp .toXMLString  (options); break;
               }

               text += "\n";

               // Copy to clipboard.

               this .copyToClipboard (text) .then (() =>
               {
                  this .setDescription (gettext("Viewpoint copied to clipboard."));

                  console .log ("Viewpoint copied to clipboard.");
                  console .debug (text);
               });
            }

            break;
         }
      }
   },
   [_keyup] (event)
   {
      //console .log (event .which);

      switch (event .which)
      {
         case 16: // Shift
         {
            this ._shiftKey = false;
            break;
         }
         case 17: // Ctrl
         {
            this ._controlKey = false;
            break;
         }
         case 18: // Alt
         {
            this ._altKey = false;
            break;
         }
         case 91: // Command
         {
            this ._commandKey = false;
            break;
         }
         case 225: // Alt Gr
         {
            this ._altGrKey = false;
            break;
         }
      }
   },
   async copyToClipboard (text)
   {
      try
      {
         await navigator .clipboard .writeText (text);
      }
      catch
      {
         // The textarea must be visible to make copy work.
         const tmp = X3DCoreContext_$("<textarea></textarea>");
         this .getShadow () .find (".x_ite-private-browser") .prepend (tmp);
         tmp .text (text) .trigger ("select");
         document .execCommand ("copy");
         tmp .remove ();
      }
   },
   dispose ()
   {
      this .getElement () .off (".X3DCoreContext .ContextMenu");

      this [_context] .getExtension ("WEBGL_lose_context") ?.loseContext ?.();
      this [_shadow] .find ("*") .remove ();
   },
});

const X3DCoreContext_default_ = X3DCoreContext;
;

/* harmony default export */ const Core_X3DCoreContext = (Namespace/* default */.A .add ("X3DCoreContext", X3DCoreContext_default_));
;// ./src/x_ite/Components/Texturing/TextureProperties.js







function TextureProperties (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TextureProperties);

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.3)
   {
      this ._minificationFilter  = "FASTEST";
      this ._magnificationFilter = "FASTEST";
      this ._textureCompression  = "FASTEST";
   }
}

Object .assign (Object .setPrototypeOf (TextureProperties .prototype, Core_X3DNode .prototype),
{
   getBorderWidth ()
   {
      // https://stackoverflow.com/questions/27760277/webgl-border-color-shader?lq=1
      return Math_Algorithm .clamp (this ._borderWidth .getValue (), 0, 1);
   },
   getBoundaryMode: (() =>
   {
      const boundaryModes = new Map ([
         ["CLAMP",             "CLAMP_TO_EDGE"], // "CLAMP"
         ["CLAMP_TO_EDGE",     "CLAMP_TO_EDGE"],
         ["CLAMP_TO_BOUNDARY", "CLAMP_TO_EDGE"], // "CLAMP_TO_BORDER"
         ["MIRRORED_REPEAT",   "MIRRORED_REPEAT"],
         ["REPEAT",            "REPEAT"],
      ]);

      return function (string)
      {
         return boundaryModes .get (string) ?? "REPEAT";
      };
   })(),
   getBoundaryModeS ()
   {
      return this .getBoundaryMode (this ._boundaryModeS .getValue ());
   },
   getBoundaryModeT ()
   {
      return this .getBoundaryMode (this ._boundaryModeT .getValue ());
   },
   getBoundaryModeR ()
   {
      return this .getBoundaryMode (this ._boundaryModeR .getValue ());
   },
   getMinificationFilter: (() =>
   {
      const minificationFilters = new Map ([
         ["AVG_PIXEL_AVG_MIPMAP",         ["LINEAR",  "LINEAR_MIPMAP_LINEAR"]],
         ["AVG_PIXEL",                    ["LINEAR",  "LINEAR"]],
         ["AVG_PIXEL_NEAREST_MIPMAP",     ["LINEAR",  "LINEAR_MIPMAP_NEAREST"]],
         ["NEAREST_PIXEL_AVG_MIPMAP",     ["NEAREST", "NEAREST_MIPMAP_LINEAR"]],
         ["NEAREST_PIXEL_NEAREST_MIPMAP", ["NEAREST", "NEAREST_MIPMAP_NEAREST"]],
         ["NEAREST_PIXEL",                ["NEAREST", "NEAREST"]],
         ["NICEST",                       ["LINEAR",  "LINEAR_MIPMAP_LINEAR"]],
         ["FASTEST",                      ["NEAREST", "NEAREST"]],
      ]);

      return function (mipMaps = true)
      {
         const i = mipMaps && this ._generateMipMaps .getValue () ? 1 : 0;

         return minificationFilters .get (this ._minificationFilter .getValue ()) ?.[i]
            ?? this .getBrowser () .getDefaultTextureProperties () .getMinificationFilter (mipMaps);
      };
   })(),
   getMagnificationFilter: (() =>
   {
      const magnificationFilters = new Map ([
         ["AVG_PIXEL",     "LINEAR"],
         ["NEAREST_PIXEL", "NEAREST"],
         ["NICEST",        "LINEAR"],
         ["FASTEST",       "NEAREST"],
      ]);

      return function ()
      {
         return magnificationFilters .get (this ._magnificationFilter .getValue ())
            ?? this .getBrowser () .getDefaultTextureProperties () .getMagnificationFilter ();
      };
   })(),
   getTextureCompression: (() =>
   {
      const textureCompressions = new Map ([
         ["DEFAULT", "RGBA"],
         ["NICEST",  "RGBA"],
         ["FASTEST", "RGBA"],
         ["LOW",     "RGBA"],
         ["MEDIUM",  "RGBA"],
         ["HIGH",    "RGBA"],
      ]);

      return function ()
      {
         const
            browser            = this .getBrowser (),
            gl                 = browser .getContext (),
            compressedTexture  = gl .getExtension ("WEBGL_compressed_texture_etc"), // TODO: find suitable compression.
            textureCompression = compressedTexture ?.[textureCompressions .get (this ._textureCompression .getValue ())];

         return textureCompression ?? gl .RGBA;
      };
   })(),
});

Object .defineProperties (TextureProperties,
{
   ... Core_X3DNode .getStaticProperties ("TextureProperties", "Texturing", 2, "textureProperties", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "borderColor",         new x_ite_Fields .SFColorRGBA ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "borderWidth",         new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "anisotropicDegree",   new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "generateMipMaps",     new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "minificationFilter",  new x_ite_Fields .SFString ("DEFAULT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "magnificationFilter", new x_ite_Fields .SFString ("DEFAULT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "boundaryModeS",       new x_ite_Fields .SFString ("REPEAT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "boundaryModeT",       new x_ite_Fields .SFString ("REPEAT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "boundaryModeR",       new x_ite_Fields .SFString ("REPEAT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "textureCompression",  new x_ite_Fields .SFString ("DEFAULT")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texturePriority",     new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const TextureProperties_default_ = TextureProperties;
;

/* harmony default export */ const Texturing_TextureProperties = (Namespace/* default */.A .add ("TextureProperties", TextureProperties_default_));
;// ./src/x_ite/Browser/EnvironmentalEffects/X3DEnvironmentalEffectsContext.js


const _backgroundTextureProperties = Symbol ();

function X3DEnvironmentalEffectsContext () { }

Object .assign (X3DEnvironmentalEffectsContext .prototype,
{
   getBackgroundTextureProperties ()
   {
      return this [_backgroundTextureProperties] ??= (() =>
      {
         const backgroundTextureProperties = new Texturing_TextureProperties (this .getPrivateScene ());

         backgroundTextureProperties ._boundaryModeS       = "CLAMP_TO_EDGE";
         backgroundTextureProperties ._boundaryModeT       = "CLAMP_TO_EDGE";
         backgroundTextureProperties ._boundaryModeR       = "CLAMP_TO_EDGE";
         backgroundTextureProperties ._minificationFilter  = "NICEST";
         backgroundTextureProperties ._magnificationFilter = "NICEST";
         backgroundTextureProperties ._textureCompression  = "DEFAULT";
         backgroundTextureProperties .setup ();

         return backgroundTextureProperties;
      })();
   },
});

const X3DEnvironmentalEffectsContext_default_ = X3DEnvironmentalEffectsContext;
;

/* harmony default export */ const EnvironmentalEffects_X3DEnvironmentalEffectsContext = (Namespace/* default */.A .add ("X3DEnvironmentalEffectsContext", X3DEnvironmentalEffectsContext_default_));
;// ./src/x_ite/Components/Rendering/X3DComposedGeometryNode.js





function X3DComposedGeometryNode (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DComposedGeometryNode);

   // Private properties

   this .polygons  = [ ];
   this .triangles = [ ];
}

Object .assign (Object .setPrototypeOf (X3DComposedGeometryNode .prototype, Rendering_X3DGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometryNode .prototype .initialize .call (this);

      this ._attrib   .addInterest ("set_attrib__",   this);
      this ._fogCoord .addInterest ("set_fogCoord__", this);
      this ._color    .addInterest ("set_color__",    this);
      this ._texCoord .addInterest ("set_texCoord__", this);
      this ._normal   .addInterest ("set_normal__",   this);
      this ._tangent  .addInterest ("set_tangent__",  this);
      this ._coord    .addInterest ("set_coord__",    this);

      this .set_attrib__ ();
      this .set_fogCoord__ ();
      this .set_color__ ();
      this .set_texCoord__ ();
      this .set_tangent__ ();
      this .set_normal__ ();
      this .set_coord__ ();
   },
   checkVertexCount (numVertices, multiplier)
   {
      return numVertices - numVertices % multiplier;
   },
   getPolygonIndex (index)
   {
      return index;
   },
   getTriangleIndex (index)
   {
      return index;
   },
   createPolygons (polygonsSize, polygons = [ ])
   {
      for (let i = 0; i < polygonsSize; ++ i)
         polygons .push (this .getPolygonIndex (i));

      return polygons;
   },
   createTriangles (trianglesSize, triangles = [ ])
   {
      for (let i = 0; i < trianglesSize; ++ i)
         triangles .push (this .getTriangleIndex (i));

      return triangles;
   },
   build (verticesPerPolygon, polygonsSize, verticesPerFace, trianglesSize)
   {
      if (!this .coordNode ?.getSize ())
         return;

      // Set size to a multiple of verticesPerPolygon.

      polygonsSize  = this .checkVertexCount (polygonsSize,  verticesPerPolygon);
      trianglesSize = this .checkVertexCount (trianglesSize, verticesPerFace);

      const
         colorPerVertex     = this ._colorPerVertex .getValue (),
         normalPerVertex    = this ._normalPerVertex .getValue (),
         coordIndicesArray  = this .getCoordIndices (),
         attribNodes        = this .getAttrib (),
         numAttribNodes     = attribNodes .length,
         attribArrays       = this .getAttribs (),
         fogCoordNode       = this .getFogCoord (),
         colorNode          = this .getColor (),
         texCoordNode       = this .getTexCoord (),
         tangentNode        = this .getTangent (),
         normalNode         = this .getNormal (),
         coordNode          = this .getCoord (),
         fogDepthArray      = this .getFogDepths (),
         colorArray         = this .getColors (),
         multiTexCoordArray = this .getMultiTexCoords (),
         tangentArray       = this .getTangents (),
         normalArray        = this .getNormals (),
         vertexArray        = this .getVertices (),
         polygons           = this .createPolygons (polygonsSize, this .polygons),
         triangles          = this .createTriangles (trianglesSize, this .triangles);

      // Init TextureCoordinateNode.

      texCoordNode ?.init (multiTexCoordArray);

      // Fill GeometryNode.

      for (let i = 0; i < trianglesSize; ++ i)
      {
         const
            face  = Math .floor (i / verticesPerFace),
            index = polygons [triangles [i]];

         coordIndicesArray .push (index);

         for (let a = 0; a < numAttribNodes; ++ a)
            attribNodes [a] .addValue (index, attribArrays [a]);

         fogCoordNode ?.addDepth (index, fogDepthArray);

         colorNode ?.addColor (colorPerVertex ? index : face, colorArray);

         texCoordNode ?.addPoint (index, multiTexCoordArray);

         tangentNode ?.addVector (normalPerVertex ? index : face, tangentArray);
         normalNode  ?.addVector (normalPerVertex ? index : face, normalArray);

         coordNode .addPoint (index, vertexArray);
      }

      // Autogenerate normal if not specified.

      if (!normalNode)
         this .generateNormals (verticesPerPolygon, polygonsSize, polygons, trianglesSize, triangles);

      this .setSolid (this ._solid .getValue ());
      this .setCCW (this ._ccw .getValue ());

      // Clear arrays.

      polygons  .length = 0;
      triangles .length = 0;
   },
   generateNormals (verticesPerPolygon, polygonsSize, polygons, trianglesSize, triangles)
   {
      const
         normals     = this .createNormals (verticesPerPolygon, polygonsSize, polygons),
         normalArray = this .getNormals ();

      for (let i = 0; i < trianglesSize; ++ i)
      {
         const { x, y, z } = normals [triangles [i]];

         normalArray .push (x, y, z);
      }
   },
   createNormals (verticesPerPolygon, polygonsSize, polygons = this .createPolygons (polygonsSize))
   {
      // This function is used by Sunrize.

      const normals = this .createFaceNormals (verticesPerPolygon, polygonsSize, polygons);

      if (!this ._normalPerVertex .getValue ())
         return normals;

      const normalIndex = new Map ();

      for (let i = 0; i < polygonsSize; ++ i)
      {
         const index = polygons [i];

         let pointIndex = normalIndex .get (index);

         if (!pointIndex)
            normalIndex .set (index, pointIndex = [ ]);

         pointIndex .push (i);
      }

      return this .refineNormals (normalIndex, normals, Math .PI);
   },
   createFaceNormals (verticesPerPolygon, polygonsSize, polygons = this .createPolygons (polygonsSize))
   {
      // This function is used by Sunrize.

      const
         cw      = !this ._ccw .getValue (),
         coord   = this .coordNode,
         normals = [ ];

      for (let index = 0; index < polygonsSize; index += verticesPerPolygon)
      {
         let normal;

         switch (verticesPerPolygon)
         {
            case 3:
            {
               normal = coord .getNormal (polygons [index],
                                          polygons [index + 1],
                                          polygons [index + 2]);
               break;
            }
            case 4:
            {
               normal = coord .getQuadNormal (polygons [index],
                                              polygons [index + 1],
                                              polygons [index + 2],
                                              polygons [index + 3]);
               break;
            }
            default:
            {
               normal = this .getPolygonNormal (index, verticesPerPolygon, polygons, coord);
               break;
            }
         }

         if (cw)
            normal .negate ();

         for (let n = 0; n < verticesPerPolygon; ++ n)
            normals .push (normal);
      }

      return normals;
   },
   getPolygonNormal: (() =>
   {
      let
         current = new Numbers_Vector3 (),
         next    = new Numbers_Vector3 ();

      return function (index, verticesPerPolygon, polygons, coord)
      {
         // Determine polygon normal.
         // We use Newell's method https://www.opengl.org/wiki/Calculating_a_Surface_Normal here:

         const normal = new Numbers_Vector3 ();

         coord .get1Point (polygons [index], next);

         for (let i = 0; i < verticesPerPolygon; ++ i)
         {
            const tmp = current;
            current = next;
            next    = tmp;

            coord .get1Point (polygons [index + (i + 1) % verticesPerPolygon], next);

            normal .x += (current .y - next .y) * (current .z + next .z);
            normal .y += (current .z - next .z) * (current .x + next .x);
            normal .z += (current .x - next .x) * (current .y + next .y);
         }

         return normal .normalize ();
      };
   })(),
});

Object .defineProperties (X3DComposedGeometryNode, Core_X3DNode .getStaticProperties ("X3DComposedGeometryNode", "Rendering", 1));

const X3DComposedGeometryNode_default_ = X3DComposedGeometryNode;
;

/* harmony default export */ const Rendering_X3DComposedGeometryNode = (Namespace/* default */.A .add ("X3DComposedGeometryNode", X3DComposedGeometryNode_default_));
;// ./src/x_ite/Components/Geometry3D/IndexedFaceSet.js









function IndexedFaceSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .IndexedFaceSet);

   // Units

   this ._creaseAngle .setUnit ("angle");
}

Object .assign (Object .setPrototypeOf (IndexedFaceSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._set_colorIndex    .addFieldInterest (this ._colorIndex);
      this ._set_texCoordIndex .addFieldInterest (this ._texCoordIndex);
      this ._set_normalIndex   .addFieldInterest (this ._normalIndex);
      this ._set_coordIndex    .addFieldInterest (this ._coordIndex);
   },
   getTexCoordPerVertexIndex (index)
   {
      if (index < this ._texCoordIndex .length)
         return this ._texCoordIndex [index];

      return this ._coordIndex [index];
   },
   getColorPerVertexIndex (index)
   {
      if (index < this ._colorIndex .length)
         return this ._colorIndex [index];

      return this ._coordIndex [index];
   },
   getColorPerFaceIndex (index)
   {
      if (index < this ._colorIndex .length)
         return this ._colorIndex [index];

      return index;
   },
   getNormalPerVertexIndex (index)
   {
      if (index < this ._normalIndex .length)
         return this ._normalIndex [index];

      return this ._coordIndex [index];
   },
   getNormalPerFaceIndex (index)
   {
      if (index < this ._normalIndex .length)
         return this ._normalIndex [index];

      return index;
   },
   build ()
   {
      // Triangulate

      const polygons = this .triangulate ();

      // Build arrays

      if (polygons .length === 0)
         return;

      // Fill GeometryNode

      const
         colorPerVertex     = this ._colorPerVertex .getValue (),
         normalPerVertex    = this ._normalPerVertex .getValue (),
         coordIndex         = this ._coordIndex .getValue (),
         coordIndicesArray  = this .getCoordIndices (),
         attribNodes        = this .getAttrib (),
         numAttribNodes     = attribNodes .length,
         attribArrays       = this .getAttribs (),
         fogCoordNode       = this .getFogCoord (),
         colorNode          = this .getColor (),
         texCoordNode       = this .getTexCoord (),
         tangentNode        = this .getTangent (),
         normalNode         = this .getNormal (),
         coordNode          = this .getCoord (),
         fogDepthArray      = this .getFogDepths (),
         colorArray         = this .getColors (),
         multiTexCoordArray = this .getMultiTexCoords (),
         tangentArray       = this .getTangents (),
         normalArray        = this .getNormals (),
         vertexArray        = this .getVertices ();

      texCoordNode ?.init (multiTexCoordArray);

      for (const { triangles, face } of polygons)
      {
         for (const i of triangles)
         {
            const index = coordIndex [i];

            coordIndicesArray .push (index);

            for (let a = 0; a < numAttribNodes; ++ a)
               attribNodes [a] .addValue (index, attribArrays [a]);

            fogCoordNode ?.addDepth (index, fogDepthArray);

            colorNode ?.addColor (colorPerVertex ? this .getColorPerVertexIndex (i) : this .getColorPerFaceIndex (face), colorArray);

            texCoordNode ?.addPoint (this .getTexCoordPerVertexIndex (i), multiTexCoordArray);

            tangentNode ?.addVector (normalPerVertex ? this .getNormalPerVertexIndex (i) : this .getNormalPerFaceIndex (face), tangentArray);
            normalNode  ?.addVector (normalPerVertex ? this .getNormalPerVertexIndex (i) : this .getNormalPerFaceIndex (face), normalArray);

            coordNode .addPoint (index, vertexArray);
         }
      }

      // Autogenerate normals if not specified.

      if (!normalNode)
         this .generateNormals (polygons);

      this .setSolid (this ._solid .getValue ());
      this .setCCW (this ._ccw .getValue ());
   },
   triangulate ()
   {
      const
         convex      = this ._convex .getValue (),
         coordLength = this ._coordIndex .length,
         polygons    = [ ];

      if (!this .getCoord ())
         return polygons;

      // Add -1 (polygon end marker) to coordIndex if not present.
      if (coordLength && this ._coordIndex .at (-1) > -1)
         this ._coordIndex .push (-1);

      if (coordLength)
      {
         const
            coordIndex  = this ._coordIndex .getValue (),
            coordLength = this ._coordIndex .length;

         // Construct triangle array and determine the number of used points.

         for (let i = 0, face = 0, vertices = [ ]; i < coordLength; ++ i)
         {
            const index = coordIndex [i];

            if (index > -1)
            {
               // Add vertex index.
               vertices .push (i);
            }
            else
            {
               // Negative index.

               switch (vertices .length)
               {
                  case 0:
                  case 1:
                  case 2:
                  {
                     vertices .length = 0;
                     break;
                  }
                  case 3:
                  {
                     // Add polygon with one triangle.
                     polygons .push ({ vertices: vertices, triangles: vertices, face: face });
                     vertices = [ ];
                     break;
                  }
                  default:
                  {
                     // Triangulate polygon.

                     const
                        triangles = [ ],
                        polygon   = { vertices: vertices, triangles: triangles, face: face };

                     if (convex)
                        Geometry_Triangle3 .triangulateConvexPolygon (vertices, triangles);
                     else
                        this .triangulatePolygon (vertices, triangles);

                     if (triangles .length < 3)
                     {
                        vertices .length = 0;
                     }
                     else
                     {
                        polygons .push (polygon);
                        vertices = [ ];
                     }

                     break;
                  }
               }

               ++ face;
            }
         }
      }

      return polygons;
   },
   triangulatePolygon: (() =>
   {
      const polygon = [ ];

      return function (vertices, triangles)
      {
         const
            coordIndex = this ._coordIndex .getValue (),
            coord      = this .getCoord (),
            length     = vertices .length;

         for (let i = polygon .length; i < length; ++ i)
            polygon .push (new Numbers_Vector3 ());

         for (let i = 0; i < length; ++ i)
         {
            const
               index = vertices [i],
               point = polygon [i];

            point .index = index;

            coord .get1Point (coordIndex [index], point);
         }

         polygon .length = length;

         Geometry_Triangle3 .triangulatePolygon (polygon, triangles);
      };
   })(),
   generateNormals (polygons)
   {
      const
         normals     = this .createNormals (polygons),
         normalArray = this .getNormals ();

      for (const { triangles } of polygons)
      {
         for (const i of triangles)
            normalArray .push (... normals [i]);
      }
   },
   createNormals (polygons)
   {
      const
         cw          = !this ._ccw .getValue (),
         coordIndex  = this ._coordIndex .getValue (),
         coord       = this .getCoord (),
         normalIndex = new Map (),
         normals     = [ ];

      for (const { vertices } of polygons)
      {
         const length = vertices .length;

         let normal;

         switch (length)
         {
            case 3:
            {
               normal = coord .getNormal (coordIndex [vertices [0]],
                                          coordIndex [vertices [1]],
                                        coordIndex [vertices [2]]);
               break;
            }
            case 4:
            {
               normal = coord .getQuadNormal (coordIndex [vertices [0]],
                                              coordIndex [vertices [1]],
                                              coordIndex [vertices [2]],
                                              coordIndex [vertices [3]]);
               break;
            }
            default:
            {
               normal = this .getPolygonNormal (vertices, coordIndex, coord);
               break;
            }
         }

         if (cw)
            normal .negate ();

         // Add a normal index for each point.

         for (const index of vertices)
         {
            const point = coordIndex [index];

            let pointNormals = normalIndex .get (point);

            if (!pointNormals)
               normalIndex .set (point, pointNormals = [ ]);

            pointNormals .push (index);

            // Add this normal for each vertex.

            normals [index] = normal;
         }
      }

      if (!this ._normalPerVertex .getValue ())
         return normals;

      return this .refineNormals (normalIndex, normals, this ._creaseAngle .getValue ());
   },
   getPolygonNormal: (() =>
   {
      let
         current = new Numbers_Vector3 (),
         next    = new Numbers_Vector3 ();

      return function (vertices, coordIndex, coord)
      {
         // Determine polygon normal.
         // We use Newell's method https://www.opengl.org/wiki/Calculating_a_Surface_Normal here:

         const normal = new Numbers_Vector3 ();

         coord .get1Point (coordIndex [vertices [0]], next);

         for (let i = 0, length = vertices .length; i < length; ++ i)
         {
            const tmp = current;
            current = next;
            next    = tmp;

            coord .get1Point (coordIndex [vertices [(i + 1) % length]], next);

            normal .x += (current .y - next .y) * (current .z + next .z);
            normal .y += (current .z - next .z) * (current .x + next .x);
            normal .z += (current .x - next .x) * (current .y + next .y);
         }

         return normal .normalize ();
      };
   })(),
});

Object .defineProperties (IndexedFaceSet,
{
   ... Core_X3DNode .getStaticProperties ("IndexedFaceSet", "Geometry3D", 2, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_colorIndex",    new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_texCoordIndex", new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_normalIndex",   new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_coordIndex",    new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",               new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "convex",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "creaseAngle",       new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",    new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex",   new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorIndex",        new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "texCoordIndex",     new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalIndex",       new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "coordIndex",        new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",            new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",             new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const IndexedFaceSet_default_ = IndexedFaceSet;
;

/* harmony default export */ const Geometry3D_IndexedFaceSet = (Namespace/* default */.A .add ("IndexedFaceSet", IndexedFaceSet_default_));
;// ./src/x_ite/Components/Rendering/X3DGeometricPropertyNode.js



function X3DGeometricPropertyNode (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DGeometricPropertyNode);
}

Object .setPrototypeOf (X3DGeometricPropertyNode .prototype, Core_X3DNode .prototype);

Object .defineProperties (X3DGeometricPropertyNode, Core_X3DNode .getStaticProperties ("X3DGeometricPropertyNode", "Rendering", 1));

const X3DGeometricPropertyNode_default_ = X3DGeometricPropertyNode;
;

/* harmony default export */ const Rendering_X3DGeometricPropertyNode = (Namespace/* default */.A .add ("X3DGeometricPropertyNode", X3DGeometricPropertyNode_default_));
;// ./src/x_ite/Components/Rendering/X3DCoordinateNode.js






function X3DCoordinateNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DCoordinateNode);
}

Object .assign (Object .setPrototypeOf (X3DCoordinateNode .prototype, Rendering_X3DGeometricPropertyNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometricPropertyNode .prototype .initialize .call (this);

      this ._point .addInterest ("set_point__", this);

      this .set_point__ ();
   },
   set_point__ ()
   {
      this .point  = this ._point .getValue ();
      this .length = this ._point .length;
   },
   getSize ()
   {
      return this .length;
   },
   set1Point (index, point)
   {
      this ._point [index] = point;
   },
   get1Point (index, result)
   {
      if (index < this .length)
      {
         const point = this .point;

         index *= 3;

         return result .set (point [index], point [index + 1], point [index + 2]);
      }
      else
      {
         return result .set (0);
      }
   },
   addPoint (index, array)
   {
      if (index < this .length)
      {
         const point = this .point;

         index *= 3;

         array .push (point [index], point [index + 1], point [index + 2], 1);
      }
      else
      {
         array .push (0, 0, 0, 1);
      }
   },
   addPoints (array, min = this .length)
   {
      const
         point  = this .point,
         length = this .length * 3;

      for (let index = 0; index < length; index += 3)
         array .push (point [index], point [index + 1], point [index + 2], 1);

      min *= 3;

      if (length)
      {
         for (let index = length; index < min; index += 3)
            array .push (point [length - 3], point [length - 2], point [length - 1], 1);
      }
      else
      {
         for (let index = length; index < min; index += 3)
            array .push (0, 0, 0, 1);
      }

      return array;
   },
   getNormal: (() =>
   {
      const
         point1 = new Numbers_Vector3 (),
         point2 = new Numbers_Vector3 (),
         point3 = new Numbers_Vector3 ();

      return function (index1, index2, index3)
      {
         // The index[1,2,3] cannot be less than 0.

         const length = this .length;

         if (index1 < length && index2 < length && index3 < length)
         {
            return Geometry_Triangle3 .normal (this .get1Point (index1, point1),
                                      this .get1Point (index2, point2),
                                      this .get1Point (index3, point3),
                                      new Numbers_Vector3 ());
         }

         return new Numbers_Vector3 ();
      };
   })(),
   getQuadNormal: (() =>
   {
      const
         point1 = new Numbers_Vector3 (),
         point2 = new Numbers_Vector3 (),
         point3 = new Numbers_Vector3 (),
         point4 = new Numbers_Vector3 ();

      return function (index1, index2, index3, index4)
      {
         // The index[1,2,3,4] cannot be less than 0.

         const length = this .length;

         if (index1 < length && index2 < length && index3 < length && index4 < length)
         {
            return Geometry_Triangle3 .quadNormal (this .get1Point (index1, point1),
                                          this .get1Point (index2, point2),
                                          this .get1Point (index3, point3),
                                          this .get1Point (index4, point4),
                                          new Numbers_Vector3 ());
         }

         return new Numbers_Vector3 ();
      };
   })(),
});

Object .defineProperties (X3DCoordinateNode, Core_X3DNode .getStaticProperties ("X3DCoordinateNode", "Rendering", 1));

const X3DCoordinateNode_default_ = X3DCoordinateNode;
;

/* harmony default export */ const Rendering_X3DCoordinateNode = (Namespace/* default */.A .add ("X3DCoordinateNode", X3DCoordinateNode_default_));
;// ./src/x_ite/Components/Rendering/Coordinate.js







function Coordinate (executionContext)
{
   Rendering_X3DCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Coordinate);

   // Units

   this ._point .setUnit ("length");
}

Object .setPrototypeOf (Coordinate .prototype, Rendering_X3DCoordinateNode .prototype);

Object .defineProperties (Coordinate,
{
   ... Core_X3DNode .getStaticProperties ("Coordinate", "Rendering", 1, "coord", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "point",    new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const Coordinate_default_ = Coordinate;
;

/* harmony default export */ const Rendering_Coordinate = (Namespace/* default */.A .add ("Coordinate", Coordinate_default_));
;// ./src/x_ite/Components/Texturing/X3DTextureCoordinateNode.js




function X3DTextureCoordinateNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTextureCoordinateNode);
}

Object .setPrototypeOf (X3DTextureCoordinateNode .prototype, Rendering_X3DGeometricPropertyNode .prototype);

Object .defineProperties (X3DTextureCoordinateNode, Core_X3DNode .getStaticProperties ("X3DTextureCoordinateNode", "Texturing", 1));

const X3DTextureCoordinateNode_default_ = X3DTextureCoordinateNode;
;

/* harmony default export */ const Texturing_X3DTextureCoordinateNode = (Namespace/* default */.A .add ("X3DTextureCoordinateNode", X3DTextureCoordinateNode_default_));
;// ./src/x_ite/Components/Texturing/X3DSingleTextureCoordinateNode.js





function X3DSingleTextureCoordinateNode (executionContext)
{
   Texturing_X3DTextureCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSingleTextureCoordinateNode);
}

Object .assign (Object .setPrototypeOf (X3DSingleTextureCoordinateNode .prototype, Texturing_X3DTextureCoordinateNode .prototype),
{
   getCount ()
   {
      return 1;
   },
   init (multiArray)
   {
      // Must use new array, because there can be cloned texture coordinate nodes.
      multiArray .push (Rendering_X3DGeometryNode .createArray ());
   },
   addPoint (index, multiArray)
   {
      this .addPointToChannel (index, multiArray [0]);
   },
   getTextureCoordinateMapping (textureCoordinateMapping, channel = 0)
   {
      textureCoordinateMapping .set (this ._mapping .getValue () || channel, channel);
   },
   setShaderUniforms (gl, shaderObject, channel = 0)
   {
      gl .uniform1i (shaderObject .x3d_TextureCoordinateGenerator [channel] .mode, 0);
   },
});

Object .defineProperties (X3DSingleTextureCoordinateNode, Core_X3DNode .getStaticProperties ("X3DSingleTextureCoordinateNode", "Texturing", 1));

const X3DSingleTextureCoordinateNode_default_ = X3DSingleTextureCoordinateNode;
;

/* harmony default export */ const Texturing_X3DSingleTextureCoordinateNode = (Namespace/* default */.A .add ("X3DSingleTextureCoordinateNode", X3DSingleTextureCoordinateNode_default_));
;// ./src/x_ite/Components/Texturing/TextureCoordinate.js








function TextureCoordinate (executionContext)
{
   Texturing_X3DSingleTextureCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TextureCoordinate);
}

Object .assign (Object .setPrototypeOf (TextureCoordinate .prototype, Texturing_X3DSingleTextureCoordinateNode .prototype),
{
   initialize ()
   {
      Texturing_X3DSingleTextureCoordinateNode .prototype .initialize .call (this);

      this ._point .addInterest ("set_point__", this);

      this .set_point__ ();
   },
   set_point__ ()
   {
      this .point  = this ._point .getValue ();
      this .length = this ._point .length;
   },
   getSize ()
   {
      return this .length;
   },
   get1Point (index, result)
   {
      if (index < this .length)
      {
         const point = this .point;

         index *= 2;

         return result .set (point [index], point [index + 1], 0, 1);
      }
      else
      {
         return result .set (0, 0, 0, 1);
      }
   },
   set1Point: (() =>
   {
      const point = new Numbers_Vector2 ();

      return function (index, { x, y, w })
      {
         this ._point [index] = point .set (x, y) .divide (w);
      };
   })(),
   addPointToChannel (index, array)
   {
      if (index >= 0 && this .length)
      {
         const
            point = this .point,
            i      = (index % this .length) * 2;

         array .push (point [i], point [i + 1], 0, 1);
      }
      else
      {
         array .push (0, 0, 0, 1);
      }
   },
   addPoints (array)
   {
      const
         point  = this .point,
         length = this .length;

      for (let i = 0, p = 0; i < length; ++ i, p += 2)
         array .push (point [p], point [p + 1], 0, 1);

      return array;
   },
});

Object .defineProperties (TextureCoordinate,
{
   ... Core_X3DNode .getStaticProperties ("TextureCoordinate", "Texturing", 1, "texCoord", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mapping",  new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "point",    new x_ite_Fields .MFVec2f ()),
      ]),
      enumerable: true,
   },
});

const TextureCoordinate_default_ = TextureCoordinate;
;

/* harmony default export */ const Texturing_TextureCoordinate = (Namespace/* default */.A .add ("TextureCoordinate", TextureCoordinate_default_));
;// ./src/x_ite/Browser/Geometry3D/BoxOptions.js





function BoxOptions (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);
}

Object .assign (Object .setPrototypeOf (BoxOptions .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);
   },
   getGeometry ()
   {
      if (this .geometry)
         return this .geometry;

      this .geometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .geometry ._texCoord = new Texturing_TextureCoordinate (this .getExecutionContext ());
      this .geometry ._coord    = new Rendering_Coordinate (this .getExecutionContext ());

      const
         geometry = this .geometry,
         texCoord = this .geometry ._texCoord .getValue (),
         coord    = this .geometry ._coord .getValue ();

      geometry ._texCoordIndex = [
         0, 1, 2, 3, -1, // front
         0, 1, 2, 3, -1, // back
         0, 1, 2, 3, -1, // left
         0, 1, 2, 3, -1, // right
         0, 1, 2, 3, -1, // top
         0, 1, 2, 3, -1  // bottom
      ];

      geometry ._coordIndex = [
         0, 1, 2, 3, -1, // front
         5, 4, 7, 6, -1, // back
         1, 5, 6, 2, -1, // left
         4, 0, 3, 7, -1, // right
         4, 5, 1, 0, -1, // top
         3, 2, 6, 7, -1  // bottom
      ];

      texCoord ._point = [1, 1, 0, 1, 0, 0, 1, 0];

      coord ._point = [
         1,  1,  1,  -1, 1,  1,  -1, -1,  1,  1, -1,  1,
         1,  1, -1,  -1, 1, -1,  -1, -1, -1,  1, -1, -1,
      ];

      texCoord .setup ();
      coord    .setup ();
      geometry .setup ();

      return this .geometry;
   },
});

Object .defineProperties (BoxOptions,
{
   typeName:
   {
      value: "BoxOptions",
      enumerable: true,
   },
});

const BoxOptions_default_ = BoxOptions;
;

/* harmony default export */ const Geometry3D_BoxOptions = (Namespace/* default */.A .add ("BoxOptions", BoxOptions_default_));
;// ./src/x_ite/Components/Rendering/X3DNormalNode.js




function X3DNormalNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DNormalNode);
}

Object .setPrototypeOf (X3DNormalNode .prototype, Rendering_X3DGeometricPropertyNode .prototype);

Object .defineProperties (X3DNormalNode, Core_X3DNode .getStaticProperties ("X3DNormalNode", "Rendering", 2));

const X3DNormalNode_default_ = X3DNormalNode;
;

/* harmony default export */ const Rendering_X3DNormalNode = (Namespace/* default */.A .add ("X3DNormalNode", X3DNormalNode_default_));
;// ./src/x_ite/Components/Rendering/Normal.js







function Normal (executionContext)
{
   Rendering_X3DNormalNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Normal);
}

Object .assign (Object .setPrototypeOf (Normal .prototype, Rendering_X3DNormalNode .prototype),
{
   initialize ()
   {
      Rendering_X3DNormalNode .prototype .initialize .call (this);

      this ._vector .addInterest ("set_vector__", this);

      this .set_vector__ ();
   },
   set_vector__ ()
   {
      this .vector = this ._vector .getValue ();
      this .length = this ._vector .length;
   },
   addVector (index, array)
   {
      if (index >= 0 && this .length)
      {
         const
            vector = this .vector,
            i      = (index % this .length) * 3;

         array .push (vector [i], vector [i + 1], vector [i + 2]);
      }
      else
      {
         return array .push (0, 0, 0);
      }
   },
   addVectors (array, min = this .length)
   {
      const length = this .length;

      if (length)
      {
         const vector = this .vector;

         for (let index = 0; index < min; ++ index)
         {
            const i = (index % length) * 3;

            array .push (vector [i], vector [i + 1], vector [i + 2]);
         }
      }
      else
      {
         for (let index = 0; index < min; ++ index)
            array .push (0, 0, 0);
      }

      return array;
   },
});

Object .defineProperties (Normal,
{
   ... Core_X3DNode .getStaticProperties ("Normal", "Rendering", 2, "normal", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "vector",   new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const Normal_default_ = Normal;
;

/* harmony default export */ const Rendering_Normal = (Namespace/* default */.A .add ("Normal", Normal_default_));
;// ./src/x_ite/Browser/Geometry3D/ConeOptions.js











function ConeOptions (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addChildObjects (Base_X3DConstants .inputOutput, "dimension", new x_ite_Fields .SFInt32 (20))
}

Object .assign (Object .setPrototypeOf (ConeOptions .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .addInterest ("eventsProcessed", this);
   },
   getSideGeometry ()
   {
      if (!this .sideGeometry)
         this .build ();

      return this .sideGeometry;
   },
   getBottomGeometry ()
   {
      if (!this .bottomGeometry)
         this .build ();

      return this .bottomGeometry;
   },
   createTexCoordIndex ()
   {
      const
         dimension           = this ._dimension .getValue (),
         sideTexCoordIndex   = this .sideGeometry ._texCoordIndex,
         bottomTexCoordIndex = this .bottomGeometry ._texCoordIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideTexCoordIndex .push (i, i + 1, i + dimension + 1, -1);

      // Bottom

      for (let i = dimension - 1; i > -1; -- i)
         bottomTexCoordIndex .push (2 * (dimension + 1) + i);

      bottomTexCoordIndex .push (-1);
   },
   createTexCoord ()
   {
      const
         dimension = this ._dimension .getValue (),
         point     = this .sideGeometry ._texCoord .getValue () ._point;

      // Side Bottom

      for (let i = 0; i < dimension + 1; ++ i)
      {
         const u = i / dimension;

         point .push (new Numbers_Vector2 (u, 0));
      }

      // Side Top

      for (let i = 0; i < dimension + 1; ++ i)
      {
         const u = (i + 0.5) / dimension;

         point .push (new Numbers_Vector2 (u, 1));
      }

      // Bottom

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            t     = Numbers_Complex .fromPolar (-1, theta);

         point .push (new Numbers_Vector2 ((t .imag + 1) / 2, (t .real + 1) / 2));
      }
   },
   createNormalIndex ()
   {
      const
         dimension         = this ._dimension .getValue (),
         sideNormalIndex   = this .sideGeometry ._normalIndex,
         bottomNormalIndex = this .bottomGeometry ._normalIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideNormalIndex .push (i, (i + 1) % dimension, i + dimension, -1);

      // Bottom

      for (let i = 0; i < dimension; ++ i)
         bottomNormalIndex .push (2 * dimension);

      bottomNormalIndex .push (-1);
   },
   createNormal ()
   {
      const
         dimension = this ._dimension .getValue (),
         vector    = this .sideGeometry ._normal .getValue () ._vector,
         nz        = Numbers_Complex .fromPolar (1, -Math .PI / 4);

      // Side Bottom

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            n     = Numbers_Complex .fromPolar (nz .imag, theta);

         vector .push (new Numbers_Vector3 (n .imag, nz .real, n .real));
      }

      // Side Top

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = (i + 0.5) / dimension,
            theta = 2 * Math .PI * u,
            n    = Numbers_Complex .fromPolar (nz .imag, theta);

         vector .push (new Numbers_Vector3 (n .imag, nz .real, n .real));
      }

      // Bottom

      vector .push (new Numbers_Vector3 (0, -1, 0));
   },
   createCoordIndex ()
   {
      const
         dimension        = this ._dimension .getValue (),
         sideCoordIndex   = this .sideGeometry ._coordIndex,
         bottomCoordIndex = this .bottomGeometry ._coordIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideCoordIndex .push (i, (i + 1) % dimension, dimension, -1);

      // Bottom

      for (let i = dimension - 1; i > -1; -- i)
         bottomCoordIndex .push (i);

      bottomCoordIndex .push (-1);
   },
   createPoints ()
   {
      const
         dimension = this ._dimension .getValue (),
         point     = this .sideGeometry ._coord .getValue () ._point;

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            p     = Numbers_Complex .fromPolar (-1, theta);

         point .push (new Numbers_Vector3 (p .imag, -1, p .real));
      }

      point .push (new Numbers_Vector3 (0, 1, 0));
   },
   build ()
   {
      this .sideGeometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .sideGeometry ._texCoord = new Texturing_TextureCoordinate (this .getExecutionContext ());
      this .sideGeometry ._normal   = new Rendering_Normal (this .getExecutionContext ());
      this .sideGeometry ._coord    = new Rendering_Coordinate (this .getExecutionContext ());

      this .bottomGeometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .bottomGeometry ._texCoord = this .sideGeometry ._texCoord;
      this .bottomGeometry ._normal   = this .sideGeometry ._normal;
      this .bottomGeometry ._coord    = this .sideGeometry ._coord;

      this .createTexCoordIndex ();
      this .createTexCoord ();
      this .createNormalIndex ();
      this .createNormal ();
      this .createCoordIndex ();
      this .createPoints ();

      const
         sideGeometry   = this .sideGeometry,
         bottomGeometry = this .bottomGeometry,
         texCoord       = this .sideGeometry ._texCoord .getValue (),
         normal         = this .sideGeometry ._normal .getValue (),
         coord          = this .sideGeometry ._coord .getValue ();

      texCoord       .setup ();
      normal         .setup ();
      coord          .setup ();
      sideGeometry   .setup ();
      bottomGeometry .setup ();
   },
   eventsProcessed ()
   {
      this .sideGeometry   = null;
      this .bottomGeometry = null;
   },
});

Object .defineProperties (ConeOptions,
{
   typeName:
   {
      value: "ConeOptions",
      enumerable: true,
   },
});

const ConeOptions_default_ = ConeOptions;
;

/* harmony default export */ const Geometry3D_ConeOptions = (Namespace/* default */.A .add ("ConeOptions", ConeOptions_default_));
;// ./src/x_ite/Browser/Geometry3D/CylinderOptions.js











function CylinderOptions (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addChildObjects (Base_X3DConstants .inputOutput, "dimension", new x_ite_Fields .SFInt32 (20))
}

Object .assign (Object .setPrototypeOf (CylinderOptions .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .addInterest ("eventsProcessed", this);
   },
   getSideGeometry ()
   {
      if (!this .sideGeometry)
         this .build ();

      return this .sideGeometry;
   },
   getTopGeometry ()
   {
      if (!this .topGeometry)
         this .build ();

      return this .topGeometry;
   },
   getBottomGeometry ()
   {
      if (!this .bottomGeometry)
         this .build ();

      return this .bottomGeometry;
   },
   createTexCoordIndex ()
   {
      const
         dimension           = this ._dimension .getValue (),
         sideTexCoordIndex   = this .sideGeometry ._texCoordIndex,
         bottomTexCoordIndex = this .bottomGeometry ._texCoordIndex,
         topTexCoordIndex    = this .topGeometry ._texCoordIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideTexCoordIndex .push (i, i + 1, i + 2 + dimension, i + 1 + dimension, -1);

      // Bottom

      for (let i = dimension - 1; i > -1; -- i)
         bottomTexCoordIndex .push (2 * (dimension + 1) + i);

      bottomTexCoordIndex .push (-1);

      // Top

      for (let i = 0; i < dimension; ++ i)
         topTexCoordIndex .push (2 * (dimension + 1) + dimension + i);

      topTexCoordIndex .push (-1);
   },
   createTexCoord ()
   {
      const
         dimension = this ._dimension .getValue (),
         point     = this .sideGeometry ._texCoord .getValue () ._point;

      // Side Bottom

      for (let i = 0; i < dimension + 1; ++ i)
      {
         const u = i / dimension;

         point .push (new Numbers_Vector2 (u, 0));
      }

      // Side Top

      for (let i = 0; i < dimension + 1; ++ i)
      {
         const u = i/ dimension;

         point .push (new Numbers_Vector2 (u, 1));
      }

      // Bottom

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            t     = Numbers_Complex .fromPolar (-1, theta);

         point .push (new Numbers_Vector2 ((t .imag + 1) / 2, (t .real + 1) / 2));
      }

      // Top

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            t     = Numbers_Complex .fromPolar (1, theta);

         point .push (new Numbers_Vector2 (1 - (t .imag + 1) / 2, (t .real + 1) / 2));
      }
   },
   createNormalIndex ()
   {
      const
         dimension         = this ._dimension .getValue (),
         sideNormalIndex   = this .sideGeometry ._normalIndex,
         bottomNormalIndex = this .bottomGeometry ._normalIndex,
         topNormalIndex    = this .topGeometry ._normalIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideNormalIndex .push (i, (i + 1) % dimension, (i + 1) % dimension, i, -1);

      // Bottom

      for (let i = 0; i < dimension; ++ i)
         bottomNormalIndex .push (dimension);

      bottomNormalIndex .push (-1);

      // Top

      for (let i = 0; i < dimension; ++ i)
         topNormalIndex .push (dimension + 1);

      topNormalIndex .push (-1);
   },
   createNormal ()
   {
      const
         dimension = this ._dimension .getValue (),
         vector    = this .sideGeometry ._normal .getValue () ._vector;

      // Side

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            n     = Numbers_Complex .fromPolar (-1, theta);

         vector .push (new Numbers_Vector3 (n .imag, 0, n .real));
      }

      // Bottom

      vector .push (new Numbers_Vector3 (0, -1, 0));

      // Top

      vector .push (new Numbers_Vector3 (0, 1, 0));
   },
   createCoordIndex ()
   {
      const
         dimension        = this ._dimension .getValue (),
         sideCoordIndex   = this .sideGeometry ._coordIndex,
         bottomCoordIndex = this .bottomGeometry ._coordIndex,
         topCoordIndex    = this .topGeometry ._coordIndex;

      // Side

      for (let i = 0; i < dimension; ++ i)
         sideCoordIndex .push (i, (i + 1) % dimension, (i + 1) % dimension + dimension, i + dimension, -1);

      // Bottom

      for (let i = dimension - 1; i > -1; -- i)
         bottomCoordIndex .push (i);

      bottomCoordIndex .push (-1);

      // Top

      for (let i = 0; i < dimension; ++ i)
         topCoordIndex .push (i + dimension);

      topCoordIndex .push (-1);
   },
   createPoints ()
   {
      const
         dimension = this ._dimension .getValue (),
         point     = this .sideGeometry ._coord .getValue () ._point;

      // Bottom

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            p     = Numbers_Complex .fromPolar (-1, theta);

         point  .push (new Numbers_Vector3 (p .imag, -1, p .real));
      }

      // Top

      for (let i = 0; i < dimension; ++ i)
      {
         const
            u     = i / dimension,
            theta = 2 * Math .PI * u,
            p     = Numbers_Complex .fromPolar (-1, theta);

         point  .push (new Numbers_Vector3 (p .imag, 1, p .real));
      }
   },
   build ()
   {
      this .sideGeometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .sideGeometry ._texCoord = new Texturing_TextureCoordinate (this .getExecutionContext ());
      this .sideGeometry ._normal   = new Rendering_Normal (this .getExecutionContext ());
      this .sideGeometry ._coord    = new Rendering_Coordinate (this .getExecutionContext ());

      this .bottomGeometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .bottomGeometry ._texCoord = this .sideGeometry ._texCoord;
      this .bottomGeometry ._normal   = this .sideGeometry ._normal;
      this .bottomGeometry ._coord    = this .sideGeometry ._coord;

      this .topGeometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .topGeometry ._texCoord = this .sideGeometry ._texCoord;
      this .topGeometry ._normal   = this .sideGeometry ._normal;
      this .topGeometry ._coord    = this .sideGeometry ._coord;

      this .createTexCoordIndex ();
      this .createTexCoord ();
      this .createNormalIndex ();
      this .createNormal ();
      this .createCoordIndex ();
      this .createPoints ();

      const
         sideGeometry   = this .sideGeometry,
         bottomGeometry = this .bottomGeometry,
         topGeometry    = this .topGeometry,
         texCoord       = this .sideGeometry ._texCoord .getValue (),
         normal         = this .sideGeometry ._normal .getValue (),
         coord          = this .sideGeometry ._coord .getValue ();

      texCoord       .setup ();
      normal         .setup ();
      coord          .setup ();
      sideGeometry   .setup ();
      bottomGeometry .setup ();
      topGeometry    .setup ();
   },
   eventsProcessed ()
   {
      this .sideGeometry   = null;
      this .bottomGeometry = null;
      this .topGeometry    = null;
   },
});

Object .defineProperties (CylinderOptions,
{
   typeName:
   {
      value: "CylinderOptions",
      enumerable: true,
   },
});

const CylinderOptions_default_ = CylinderOptions;
;

/* harmony default export */ const Geometry3D_CylinderOptions = (Namespace/* default */.A .add ("CylinderOptions", CylinderOptions_default_));
;// ./src/x_ite/Browser/Geometry3D/QuadSphereOptions.js










function QuadSphereOptions (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addChildObjects (Base_X3DConstants .inputOutput, "xDimension", new x_ite_Fields .SFInt32 (32),
                          Base_X3DConstants .inputOutput, "yDimension", new x_ite_Fields .SFInt32 (15))
}

Object .assign (Object .setPrototypeOf (QuadSphereOptions .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      this .addInterest ("eventsProcessed", this);
   },
   getGeometry ()
   {
      if (!this .geometry)
         this .build ();

      return this .geometry;
   },
   createTexCoordIndex ()
   {
      const
         xDimension    = this ._xDimension .getValue () + 1,
         yDimension    = this ._yDimension .getValue (),
         texCoordIndex = this .geometry ._texCoordIndex;

      // North pole

      for (let u = 0, uLength = xDimension - 1; u < uLength; ++ u)
      {
         texCoordIndex .push (u);
         texCoordIndex .push (u + xDimension - 1);
         texCoordIndex .push (u + xDimension);
         texCoordIndex .push (-1);
      }

      // Sphere segments

      for (let p = xDimension - 1, v = 0, vLength = yDimension - 3; v < vLength; ++ v, ++ p)
      {
         for (let u = 0, uLength = xDimension - 1; u < uLength; ++ u, ++ p)
         {
            texCoordIndex .push (p);
            texCoordIndex .push (p + xDimension);
            texCoordIndex .push (p + xDimension + 1);
            texCoordIndex .push (p + 1);
            texCoordIndex .push (-1);
         }
      }

      // South pole

      let p = (yDimension - 2) * xDimension - 1;

      for (let u = 0, uLength = xDimension - 1; u < uLength; ++ u, ++ p)
      {
         texCoordIndex .push (p + xDimension);
         texCoordIndex .push (p + 1);
         texCoordIndex .push (p);
         texCoordIndex .push (-1);
      }
   },
   createTexCoord ()
   {
      const
         xDimension = this ._xDimension .getValue () + 1,
         yDimension = this ._yDimension .getValue (),
         point      = this .geometry ._texCoord .getValue () ._point;

      const poleOffset = -0.5 / (xDimension - 1);

      // North pole

      for (let u = 1; u < xDimension; ++ u)
      {
         const x = u / (xDimension - 1) + poleOffset;

         point .push (new Numbers_Vector2 (x, 1));
      }

      // Sphere segments

      for (let v = 1, vLength = yDimension - 1; v < vLength; ++ v)
      {
         const y = 1 - v / (yDimension - 1);

         for (let u = 0; u < xDimension; ++ u)
         {
            const x = u / (xDimension - 1);

            point .push (new Numbers_Vector2 (x, y));
         }
      }

      // South pole

      for (let u = 1; u < xDimension; ++ u)
      {
         const x = u / (xDimension - 1) + poleOffset;

         point .push (new Numbers_Vector2 (x, 0));
      }
   },
   createCoordIndex ()
   {
      const
         xDimension = this ._xDimension .getValue () + 1,
         yDimension = this ._yDimension .getValue (),
         coordIndex = this .geometry ._coordIndex;

      // North pole

      let u = 1;

      for (const uLength = xDimension - 1; u < uLength; ++ u)
      {
         coordIndex .push (0);
         coordIndex .push (u);
         coordIndex .push (u + 1);
         coordIndex .push (-1);
      }

      coordIndex .push (0);
      coordIndex .push (u);
      coordIndex .push (1);
      coordIndex .push (-1);

      // Sphere segments

      let p = 1;

      for (let v = 0, vLength = yDimension - 3; v < vLength; ++ v, ++ p)
      {
         for (let u = 0, uLength = xDimension - 2; u < uLength; ++ u, ++ p)
         {
            coordIndex .push (p);
            coordIndex .push (p + xDimension - 1);
            coordIndex .push (p + xDimension);
            coordIndex .push (p + 1);
            coordIndex .push (-1);
         }

         coordIndex .push (p);
         coordIndex .push (p + xDimension - 1);
         coordIndex .push (p + 1);
         coordIndex .push (p - xDimension + 2);
         coordIndex .push (-1);
      }

      // South pole

      const last = p + xDimension - 1;

      for (let u = 0, uLength = xDimension - 2; u < uLength; ++ u, ++ p)
      {
         coordIndex .push (last);
         coordIndex .push (p + 1);
         coordIndex .push (p);
         coordIndex .push (-1);
      }

      coordIndex .push (last);
      coordIndex .push (last - xDimension + 1);
      coordIndex .push (p);
      coordIndex .push (-1);
   },
   createPoints ()
   {
      const
         xDimension = this ._xDimension .getValue () + 1,
         yDimension = this ._yDimension .getValue (),
         point      = this .geometry ._coord .getValue () ._point;

      // North pole
      point .push (new Numbers_Vector3 (0, 1, 0));

      // Sphere segments
      for (let v = 1, vLength = yDimension - 1; v < vLength; ++ v)
      {
         const zPlane = Numbers_Complex .fromPolar (1, -Math .PI * v / vLength);

         for (let u = 0, uLength = xDimension - 1; u < uLength; ++ u)
         {
            const yPlane = Numbers_Complex .fromPolar (zPlane .imag, 2 * Math .PI * u / uLength);

            point .push (new Numbers_Vector3 (yPlane .imag, zPlane .real, yPlane .real));
         }
      }

      // South pole
      point .push (new Numbers_Vector3 (0, -1, 0));
   },
   build ()
   {
      this .geometry            = new Geometry3D_IndexedFaceSet (this .getExecutionContext ());
      this .geometry ._texCoord = new Texturing_TextureCoordinate (this .getExecutionContext ());
      this .geometry ._coord    = new Rendering_Coordinate (this .getExecutionContext ());

      this .createTexCoordIndex ();
      this .createTexCoord ();
      this .createCoordIndex ();
      this .createPoints ();

      const
         geometry = this .geometry,
         texCoord = this .geometry ._texCoord .getValue (),
         coord    = this .geometry ._coord .getValue ();

      geometry ._creaseAngle = Math .PI;

      texCoord .setup ();
      coord    .setup ();
      geometry .setup ();
   },
   eventsProcessed ()
   {
      this .geometry = null;
   },
});

Object .defineProperties (QuadSphereOptions,
{
   typeName:
   {
      value: "QuadSphereOptions",
      enumerable: true,
   },
});

const QuadSphereOptions_default_ = QuadSphereOptions;
;

/* harmony default export */ const Geometry3D_QuadSphereOptions = (Namespace/* default */.A .add ("QuadSphereOptions", QuadSphereOptions_default_));
;// ./src/x_ite/Browser/Geometry3D/X3DGeometry3DContext.js






const
   _boxOptions      = Symbol (),
   _coneOptions     = Symbol (),
   _cylinderOptions = Symbol (),
   _sphereOptions   = Symbol ();

function X3DGeometry3DContext () { }

Object .assign (X3DGeometry3DContext .prototype,
{
   initialize ()
   {
      this .setPrimitiveQuality3D (this .getBrowserOptions () .getPrimitiveQuality ());
   },
   getBoxOptions ()
   {
      return getOptionNode .call (this, _boxOptions, Geometry3D_BoxOptions);
   },
   getConeOptions ()
   {
      return getOptionNode .call (this, _coneOptions, Geometry3D_ConeOptions);
   },
   getCylinderOptions ()
   {
      return getOptionNode .call (this, _cylinderOptions, Geometry3D_CylinderOptions);
   },
   getSphereOptions ()
   {
      return getOptionNode .call (this, _sphereOptions, Geometry3D_QuadSphereOptions);
   },
   setPrimitiveQuality3D (primitiveQuality)
   {
      const
         cone     = this .getConeOptions (),
         cylinder = this .getCylinderOptions (),
         sphere   = this .getSphereOptions ();

      switch (primitiveQuality)
      {
         case Core_PrimitiveQuality .LOW:
         {
            cone     ._dimension  = 16;
            cylinder ._dimension  = 16;
            sphere   ._xDimension = 20;
            sphere   ._yDimension = 9;
            break;
         }
         case Core_PrimitiveQuality .MEDIUM:
         {
            cone     ._dimension  = 20;
            cylinder ._dimension  = 20;
            sphere   ._xDimension = 32;
            sphere   ._yDimension = 15;
            break;
         }
         case Core_PrimitiveQuality .HIGH:
         {
            cone     ._dimension  = 32;
            cylinder ._dimension  = 32;
            sphere   ._xDimension = 64;
            sphere   ._yDimension = 31;
            break;
         }
      }
   },
});

function getOptionNode (key, OptionNode)
{
   return this [key] ??= (() =>
   {
      const optionNode = new OptionNode (this .getPrivateScene ());

      optionNode .setup ();

      return optionNode;
   })();
}

const X3DGeometry3DContext_default_ = X3DGeometry3DContext;
;

/* harmony default export */ const Geometry3D_X3DGeometry3DContext = (Namespace/* default */.A .add ("X3DGeometry3DContext", X3DGeometry3DContext_default_));
;// ./src/x_ite/Components/Shape/X3DShapeNode.js












function X3DShapeNode (executionContext)
{
   Core_X3DChildNode     .call (this, executionContext);
   Grouping_X3DBoundedObject .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DShapeNode);

   // Set default values which are almost right in most cases.

   this .setBoundedObject (true);
   this .setPointingObject (true);
   this .setCollisionObject (true);
   this .setShadowObject (true);
   this .setVisibleObject (true);

   // Private properties

   this .bbox         = new Geometry_Box3 ();
   this .bboxSize     = new Numbers_Vector3 ();
   this .bboxCenter   = new Numbers_Vector3 ();
   this .renderPasses = Rendering_RenderPass .NONE;

   this [Rendering_RenderPass .RENDER_KEY] = this;
}

Object .assign (Object .setPrototypeOf (X3DShapeNode .prototype, Core_X3DChildNode .prototype),
   Grouping_X3DBoundedObject .prototype,
{
   initialize ()
   {
      Core_X3DChildNode     .prototype .initialize .call (this);
      Grouping_X3DBoundedObject .prototype .initialize .call (this);

      this ._transformSensors .addInterest ("set_pickableObject__", this);

      this ._pointerEvents .addInterest ("set_pointingObject__", this);
      this ._castShadow    .addInterest ("set_shadowObject__",   this);
      this ._bboxSize      .addInterest ("set_boundedObject__",  this);
      this ._bboxSize      .addInterest ("set_bbox__",           this);
      this ._bboxCenter    .addInterest ("set_bbox__",           this);
      this ._appearance    .addInterest ("set_appearance__",     this);
      this ._geometry      .addInterest ("set_geometry__",       this);

      this .set_appearance__ ();
      this .set_geometry__ ();
   },
   getGeometryType ()
   {
      return Shape_GeometryType .GEOMETRY;
   },
   getNumInstances ()
   {
      return 1;
   },
   isEnabled ()
   {
      return !!(this .getNumInstances () && (this .geometryNode || this .getGeometryType () !== Shape_GeometryType .GEOMETRY));
   },
   getBBox (bbox, shadows)
   {
      if (shadows)
      {
         if (this ._castShadow .getValue ())
            return bbox .assign (this .bbox);
         else
            return bbox .set ();
      }
      else
      {
         return bbox .assign (this .bbox);
      }
   },
   getBBoxSize ()
   {
      return this .bboxSize;
   },
   getBBoxCenter ()
   {
      return this .bboxCenter;
   },
   isTransparent ()
   {
      return this .transparent;
   },
   setTransparent (value)
   {
      // Used by ParticleSystem!
      this .transparent = !!value;
   },
   getAlphaMode ()
   {
      return this .alphaMode;
   },
   setAlphaMode (value)
   {
      // Used by ParticleSystem!
      this .alphaMode = value;
   },
   getRenderPasses ()
   {
      return this .renderPasses;
   },
   getAppearance ()
   {
      return this .appearanceNode;
   },
   getGeometry ()
   {
      return this .geometryNode;
   },
   getGeometryContext ()
   {
      return this .getGeometry ();
   },
   getShapes (shapes, modelMatrix)
   {
      if (this .isVisibleObject ())
         shapes .push ({ modelMatrix, shapeNode: this });

      return shapes;
   },
   set_bbox__ ()
   {
      if (this .isDefaultBBoxSize ())
      {
         if (this .getGeometry ())
            this .bbox .assign (this .getGeometry () .getBBox ());
         else
            this .bbox .set ();
      }
      else
      {
         this .bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
      }

      this .bboxSize   .assign (this .bbox .size);
      this .bboxCenter .assign (this .bbox .center);
   },
   set_appearance__ ()
   {
      if (this .appearanceNode)
      {
         this .appearanceNode ._alphaMode     .removeInterest ("set_transparent__",   this);
         this .appearanceNode ._transparent   .removeInterest ("set_transparent__",   this);
         this .appearanceNode ._transmission  .removeInterest ("set_transmission__",  this);
         this .appearanceNode ._volumeScatter .removeInterest ("set_volumeScatter__", this);
      }

      this .appearanceNode = Base_X3DCast (Base_X3DConstants .X3DAppearanceNode, this ._appearance)
         ?? this .getBrowser () .getDefaultAppearance ();

      this .appearanceNode ._alphaMode     .addInterest ("set_transparent__",   this);
      this .appearanceNode ._transparent   .addInterest ("set_transparent__",   this);
      this .appearanceNode ._transmission  .addInterest ("set_transmission__",  this);
      this .appearanceNode ._volumeScatter .addInterest ("set_volumeScatter__", this);

      this .set_transparent__ ();
      this .set_transmission__ ();
      this .set_volumeScatter__ ();
   },
   set_geometry__ ()
   {
      if (this .geometryNode)
      {
         this .geometryNode ._transparent  .addInterest ("set_transparent__", this);
         this .geometryNode ._bbox_changed .addInterest ("set_bbox__",        this);
      }

      this .geometryNode = Base_X3DCast (Base_X3DConstants .X3DGeometryNode, this ._geometry);

      if (this .geometryNode)
      {
         this .geometryNode ._transparent  .addInterest ("set_transparent__", this);
         this .geometryNode ._bbox_changed .addInterest ("set_bbox__",        this);
      }

      this .set_bbox__ ();
      this .set_transparent__ ();
      this .set_objects__ ();
   },
   set_transparent__ ()
   {
      // This function is overloaded in ParticleSystem and GaussianSplatsShape!

      const alphaMode = this .appearanceNode .getAlphaMode ();

      if (alphaMode === Shape_AlphaMode .AUTO)
      {
         this .transparent = !!(this .appearanceNode .isTransparent () || this .geometryNode ?.isTransparent ());
         this .alphaMode   = this .transparent ? Shape_AlphaMode .BLEND : Shape_AlphaMode .OPAQUE;
      }
      else
      {
         this .transparent = alphaMode === Shape_AlphaMode .BLEND;
         this .alphaMode   = alphaMode;
      }
   },
   set_transmission__ ()
   {
      this .set_renderPass__ (this .appearanceNode .isTransmission (), Rendering_RenderPass .TRANSMISSION_BIT);

      this [Rendering_RenderPass .TRANSMISSION_KEY] = this .appearanceNode .isTransmission () ? null : this;
   },
   set_volumeScatter__ ()
   {
      this .set_renderPass__ (this .appearanceNode .isVolumeScatter (), Rendering_RenderPass .VOLUME_SCATTER_BIT);

      this [Rendering_RenderPass .VOLUME_SCATTER_KEY] = this .appearanceNode .isVolumeScatter () ? this : null;
   },
   set_renderPass__ (value, bit)
   {
      if (value)
         this .renderPasses |= bit;
      else
         this .renderPasses &= ~bit;
   },
   set_objects__ ()
   {
      this .set_boundedObject__ ();
      this .set_pointingObject__ ();
      this .set_pickableObject__ ();
      this .set_collisionObject__ ();
      this .set_shadowObject__ ();
      this .set_visibleObject__ ();
   },
   set_boundedObject__ ()
   {
      this .setBoundedObject (this .isEnabled () || !this .isDefaultBBoxSize ());
   },
   set_pointingObject__ ()
   {
      this .setPointingObject (this .isEnabled () && this ._pointerEvents .getValue ());
   },
   set_pickableObject__ ()
   {
      this .setPickableObject (this .getTransformSensors () .size);
   },
   set_collisionObject__ ()
   {
      this .setCollisionObject (this .isEnabled ());
   },
   set_shadowObject__ ()
   {
      this .setShadowObject (this .isEnabled () && this ._castShadow .getValue ());
   },
   set_visibleObject__ ()
   {
      this .setVisibleObject (this .isEnabled ());
   },
   traverse (type, renderObject)
   {
      const geometryContext = this .getGeometryContext ();

      if (!geometryContext)
         return;

      // Needed for geometry primitives, Text with ScreenFontStyle and tools.
      geometryContext .traverseBefore ?.(type, renderObject, this);

      switch (type)
      {
         case Rendering_TraverseType .POINTER:
         {
            renderObject .addPointingShape (this);
            break;
         }
         case Rendering_TraverseType .PICKING:
         {
            this .picking (renderObject);
            break;
         }
         case Rendering_TraverseType .COLLISION:
         {
            renderObject .addCollisionShape (this);
            break;
         }
         case Rendering_TraverseType .DEPTH:
         case Rendering_TraverseType .SHADOW:
         {
            renderObject .addDepthShape (this);
            break;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            // X3DAppearanceNode traverse is needed for rendered textures.

            if (renderObject .addDisplayShape (this))
               this .appearanceNode .traverse (type, renderObject);

            break;
         }
      }

      // Needed for geometry primitives, Text with ScreenFontStyle and tools.
      geometryContext .traverseAfter ?.(type, renderObject, this);
   },
   picking (renderObject)
   {
      const modelMatrix = renderObject .getModelViewMatrix () .get ();

      for (const transformSensorNode of this .getTransformSensors ())
         transformSensorNode .collect (modelMatrix);

      if (!this .geometryNode)
         return;

      const
         browser          = this .getBrowser (),
         pickSensorStack  = browser .getPickSensors (),
         pickingHierarchy = browser .getPickingHierarchy ();

      pickingHierarchy .push (this);

      for (const pickSensorNode of pickSensorStack .at (-1))
         pickSensorNode .collect (this .geometryNode, modelMatrix, pickingHierarchy);

      pickingHierarchy .pop ();
   },
   dispose ()
   {
      Grouping_X3DBoundedObject .prototype .dispose .call (this);
      Core_X3DChildNode     .prototype .dispose .call (this);
   },
});

Object .defineProperties (X3DShapeNode, Core_X3DNode .getStaticProperties ("X3DShapeNode", "Shape", 1));

const X3DShapeNode_default_ = X3DShapeNode;
;

/* harmony default export */ const Shape_X3DShapeNode = (Namespace/* default */.A .add ("X3DShapeNode", X3DShapeNode_default_));
;// ./src/x_ite/Components/Shape/Shape.js







function Shape (executionContext)
{
   Shape_X3DShapeNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Shape);
}

Object .assign (Object .setPrototypeOf (Shape .prototype, Shape_X3DShapeNode .prototype),
{
   getShapeKey ()
   {
      return 0;
   },
   getNumInstances ()
   {
      return 1;
   },
   displaySimple (gl, renderContext, shaderNode)
   {
      this .getGeometry () .displaySimple (gl, renderContext, shaderNode);
   },
   display (gl, renderContext)
   {
      this .getGeometry () .display (gl, renderContext);
   },
});

Object .defineProperties (Shape,
{
   ... Core_X3DNode .getStaticProperties ("Shape", "Shape", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "pointerEvents", new x_ite_Fields .SFBool (true)), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "castShadow",    new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",   new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",      new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "appearance",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "geometry",      new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const Shape_default_ = Shape;
;

/* harmony default export */ const Shape_Shape = (Namespace/* default */.A .add ("Shape", Shape_default_));
;// ./src/x_ite/Components/Rendering/X3DLineGeometryNode.js








const
   _numLines0            = Symbol (),
   _numLines1            = Symbol (),
   _numLines2            = Symbol (),
   _lineTrianglesBuffer0 = Symbol (),
   _lineTrianglesBuffer1 = Symbol (),
   _lineTrianglesBuffer2 = Symbol ();

function X3DLineGeometryNode (executionContext)
{
   if (!this .getExecutionContext ())
      Rendering_X3DGeometryNode .call (this, executionContext);

   // Private properties

   const
      browser = this .getBrowser (),
      gl      = browser .getContext ();

   this .lineStipples                = new Float32Array ();
   this .lineStippleBuffer           = gl .createBuffer ();
   this .thickLinesVertexArrayObject = new Rendering_VertexArray (gl);

   this .setGeometryType (1);
   this .setSolid (false);
}

Object .assign (Object .setPrototypeOf (X3DLineGeometryNode .prototype, Rendering_X3DGeometryNode .prototype),
{
   intersectsLine ()
   {
      return false;
   },
   updateVertexArrays ()
   {
      Rendering_X3DGeometryNode .prototype .updateVertexArrays .call (this);

      this .thickLinesVertexArrayObject .update ();
   },
   generateTexCoords ()
   {
      // Line stipple support.

      const numLines = this .getVertices () .length / 8;

      if (this .lineStipples .length / 6 === numLines)
         return;

      const gl = this .getBrowser () .getContext ();

      this .lineStipples = new Float32Array (numLines * 6);

      gl .bindBuffer (gl .ARRAY_BUFFER, this .lineStippleBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, this .lineStipples, gl .DYNAMIC_DRAW);
   },
   updateLengthSoFar: (() =>
   {
      const
         modelViewProjectionMatrix = new Numbers_Matrix4 (),
         point0                    = new Numbers_Vector4 (),
         point1                    = new Numbers_Vector4 (),
         projectedPoint0           = new Numbers_Vector2 (),
         projectedPoint1           = new Numbers_Vector2 ();

      return function (gl, { viewport, renderObject, modelViewMatrix })
      {
         const
            projectionMatrix = renderObject .getProjectionMatrix () .get (),
            lineStipples     = this .lineStipples,
            vertices         = this .getVertices (),
            numVertices      = vertices .length;

         modelViewProjectionMatrix .assign (modelViewMatrix) .multRight (projectionMatrix);

         let lengthSoFar = 0;

         for (let i = 0, l = 0; i < numVertices; i += 8, l += 6)
         {
            point0 .set (vertices [i],     vertices [i + 1], vertices [i + 2], vertices [i + 3]);
            point1 .set (vertices [i + 4], vertices [i + 5], vertices [i + 6], vertices [i + 7]);

            Geometry_ViewVolume .projectPointMatrix (point0, modelViewProjectionMatrix, viewport, projectedPoint0);
            Geometry_ViewVolume .projectPointMatrix (point1, modelViewProjectionMatrix, viewport, projectedPoint1);

            lineStipples [l]     = projectedPoint1 .x;
            lineStipples [l + 1] = projectedPoint1 .y;

            lineStipples [l + 3] = projectedPoint0 .x;
            lineStipples [l + 4] = projectedPoint0 .y;
            lineStipples [l + 5] = lengthSoFar;

            lengthSoFar += projectedPoint1 .subtract (projectedPoint0) .norm ();
         }

         gl .bindBuffer (gl .ARRAY_BUFFER, this .lineStippleBuffer);
         gl .bufferData (gl .ARRAY_BUFFER, lineStipples, gl .DYNAMIC_DRAW);
      };
   })(),
   displaySimple (gl, renderContext, shaderNode)
   {
      if (this .displaySimpleThick (gl, renderContext, shaderNode))
         return;

      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         shaderNode .enableLineStippleAttribute (gl, this .lineStippleBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
      }

      gl .drawArrays (gl .LINES, 0, this .vertexCount);
      gl .lineWidth (1);
   },
   displaySimpleThick (gl, renderContext, shaderNode)
   {
      const
         shapeNode          = renderContext .shapeNode,
         linePropertiesNode = shapeNode .getAppearance () .getStyleProperties (1);

      if (!linePropertiesNode)
         return false;

      if (!linePropertiesNode .getTransformLines ())
         return false;

      // Check if pass 1 and/or pass 2 are already run.

      const numLines = this .getVertices () .length / 8;

      if (this [_numLines0] !== numLines)
         return true;

      if (this .hasNormals && this [_numLines2] !== numLines)
         return true;

      // Setup vertex attributes.

      if (this .thickLinesVertexArrayObject .enable (shaderNode .getProgram ()))
      {
         if (this .hasNormals)
         {
            const
               stride       = (9 + 4 + 3) * Float32Array .BYTES_PER_ELEMENT,
               normalOffset = 13 * Float32Array .BYTES_PER_ELEMENT;

            shaderNode .enableNormalAttribute (gl, this .lineTrianglesBuffer2, stride, normalOffset);
         }

         const
            stride            = 13 * Float32Array .BYTES_PER_ELEMENT,
            coordIndexOffset  = 0,
            lineStippleOffset = 1 * Float32Array .BYTES_PER_ELEMENT,
            vertexOffset      = 9 * Float32Array .BYTES_PER_ELEMENT;

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this [_lineTrianglesBuffer0], stride, coordIndexOffset);

         shaderNode .enableLineStippleAttribute (gl, this [_lineTrianglesBuffer0], stride, lineStippleOffset);
         shaderNode .enableVertexAttribute      (gl, this [_lineTrianglesBuffer0], stride, vertexOffset);
      }

      gl .drawArrays (gl .TRIANGLES, 0, this .vertexCount * 3);

      return true;
   },
   displaySimpleInstanced (gl, shaderNode, shapeNode)
   {
      if (this .displaySimpleInstancedThick (gl, shaderNode, shapeNode))
         return;

      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, matrixOffset, normalMatrixOffset } = shapeNode;

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableLineStippleAttribute (gl, this .lineStippleBuffer, 0, 0);
         shaderNode .enableVertexAttribute      (gl, this .vertexBuffer,    0, 0);

         this .updateInstances = false;
      }

      gl .drawArraysInstanced (gl .LINES, 0, this .vertexCount, shapeNode .getNumInstances ());
   },
   displaySimpleInstancedThick (gl, shaderNode, shapeNode)
   {
      const linePropertiesNode = shapeNode .getAppearance () .getStyleProperties (1);

      if (!linePropertiesNode)
         return false;

      if (!linePropertiesNode .getTransformLines ())
         return false;

      // Check if pass 1 and/or pass 2 are already run.

      const numLines = this .getVertices () .length / 8 * shapeNode .getNumInstances ();

      if (shapeNode [_numLines0] !== numLines)
         return true;

      if (this .hasNormals && shapeNode [_numLines2] !== numLines)
         return true;

      // Setup vertex attributes.

      const instances = shapeNode .getInstances ();

      if (instances .thickLinesVertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         shaderNode .enableInstanceMatrixAttribute (gl, shapeNode [_lineTrianglesBuffer1], 0, 0, 0);

         if (this .hasNormals)
         {
            const
               stride             = (9 + 4 + 3) * Float32Array .BYTES_PER_ELEMENT,
               normalMatrixOffset = 0,
               normalOffset       = 13 * Float32Array .BYTES_PER_ELEMENT;

            if (shapeNode .normalMatrixOffset)
               shaderNode .enableInstanceNormalMatrixAttribute (gl, shapeNode [_lineTrianglesBuffer2], stride, normalMatrixOffset, 0);

            shaderNode .enableNormalAttribute (gl, shapeNode [_lineTrianglesBuffer2], stride, normalOffset);
         }

         const
            stride            = 13 * Float32Array .BYTES_PER_ELEMENT,
            coordIndexOffset  = 0,
            lineStippleOffset = 1 * Float32Array .BYTES_PER_ELEMENT,
            vertexOffset      = 9 * Float32Array .BYTES_PER_ELEMENT;

         // for (let i = 0, length = attribNodes .length; i < length; ++ i)
         //    attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, coordIndexOffset);

         shaderNode .enableLineStippleAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, lineStippleOffset);
         shaderNode .enableVertexAttribute      (gl, shapeNode [_lineTrianglesBuffer0], stride, vertexOffset);

         this .updateInstances = false;
      }

      gl .drawArrays (gl .TRIANGLES, 0, this .vertexCount * 3 * shapeNode .getNumInstances ());

      return true;
   },
   display (gl, renderContext)
   {
      if (this .displayThick (gl, renderContext))
         return;

      const
         { viewport, appearanceNode } = renderContext,
         browser         = this .getBrowser (),
         primitiveMode   = browser .getPrimitiveMode (gl .LINES),
         shaderNode      = appearanceNode .getShader (this, renderContext),
         renderModeNodes = appearanceNode .getRenderModes ();

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Setup shader.

      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this);

      // Setup vertex attributes.

      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         shaderNode .enableLineStippleAttribute (gl, this .lineStippleBuffer, 0, 0);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (this .colorMaterial)
            shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);

         if (this .hasTangents)
            shaderNode .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
      }

      gl .drawArrays (primitiveMode, 0, this .vertexCount);

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();

      gl .lineWidth (1);
   },
   displayThick: (() =>
   {
      const
         matrix                            = new Numbers_Matrix4 (),
         modelViewProjectionMatrixArray    = new Float32Array (16),
         invModelViewProjectionMatrixArray = new Float32Array (16);

      return function (gl, renderContext)
      {
         const
            { renderObject, viewport, modelViewMatrix, appearanceNode } = renderContext,
            linePropertiesNode = appearanceNode .getStyleProperties (1);

         if (!linePropertiesNode)
            return false;

         // Also important for display.
         if (linePropertiesNode .getApplied () && linePropertiesNode .getLinetype () !== 1)
            this .updateLengthSoFar (gl, renderContext);

         if (!linePropertiesNode .getTransformLines ())
            return false;

         const
            browser             = this .getBrowser (),
            shaderNode          = appearanceNode .getShader (this, renderContext),
            renderModeNodes     = appearanceNode .getRenderModes (),
            projectionMatrix    = renderObject .getProjectionMatrix () .get (),
            primitiveMode       = browser .getPrimitiveMode (gl .TRIANGLES),
            transformShaderNode = browser .getLineTransformShader ();

         modelViewProjectionMatrixArray .set (matrix .assign (modelViewMatrix) .multRight (projectionMatrix));
         invModelViewProjectionMatrixArray .set (matrix .inverse ());

         // Pass 1

         transformShaderNode .enable (gl);

         gl .uniform4f (transformShaderNode .viewport, ... viewport);
         gl .uniformMatrix4fv (transformShaderNode .modelViewProjectionMatrix,    false, modelViewProjectionMatrixArray);
         gl .uniformMatrix4fv (transformShaderNode .invModelViewProjectionMatrix, false, invModelViewProjectionMatrixArray);
         gl .uniform1f (transformShaderNode .linewidthScaleFactor1_2, linePropertiesNode .getLinewidthScaleFactor () / 2);

         // Setup vertex attributes.

         if (this .thickLinesVertexArrayObject .enable (transformShaderNode .getProgram ()))
         {
            const
               coordIndexStride  = 2 * Uint32Array .BYTES_PER_ELEMENT,
               coordIndexOffset0 = 0,
               coordIndexOffset1 = 1 * Uint32Array .BYTES_PER_ELEMENT,
               lengthSoFarStride = 6 * Float32Array .BYTES_PER_ELEMENT,
               lengthSoFarOffset = 5 * Float32Array .BYTES_PER_ELEMENT,
               fogDepthStride    = 2 * Float32Array .BYTES_PER_ELEMENT,
               fogDepthOffset0   = 0,
               fogDepthOffset1   = 1 * Float32Array .BYTES_PER_ELEMENT,
               colorStride       = 8 * Float32Array .BYTES_PER_ELEMENT,
               colorOffset0      = 0,
               colorOffset1      = 4 * Float32Array .BYTES_PER_ELEMENT,
               vertexStride      = 8 * Float32Array .BYTES_PER_ELEMENT,
               vertexOffset0     = 0,
               vertexOffset1     = 4 * Float32Array .BYTES_PER_ELEMENT;

            // for (let i = 0, length = attribNodes .length; i < length; ++ i)
            //    attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

            if (this .coordIndices .length)
            {
               transformShaderNode .enableUintAttrib (gl, "x3d_CoordIndex0", this .coordIndexBuffer, 1, coordIndexStride, coordIndexOffset0);
               transformShaderNode .enableUintAttrib (gl, "x3d_CoordIndex1", this .coordIndexBuffer, 1, coordIndexStride, coordIndexOffset1);
            }

            transformShaderNode .enableFloatAttrib (gl, "x3d_LengthSoFar", this .lineStippleBuffer, 1, lengthSoFarStride, lengthSoFarOffset);

            if (this .hasFogCoords)
            {
               transformShaderNode .enableFloatAttrib (gl, "x3d_FogDepth0", this .fogDepthBuffer, 1, fogDepthStride, fogDepthOffset0);
               transformShaderNode .enableFloatAttrib (gl, "x3d_FogDepth1", this .fogDepthBuffer, 1, fogDepthStride, fogDepthOffset1);
            }

            if (this .colorMaterial)
            {
               transformShaderNode .enableFloatAttrib (gl, "x3d_Color0", this .colorBuffer, 4, colorStride, colorOffset0);
               transformShaderNode .enableFloatAttrib (gl, "x3d_Color1", this .colorBuffer, 4, colorStride, colorOffset1);
            }

            transformShaderNode .enableFloatAttrib (gl, "x3d_Vertex0", this .vertexBuffer, 4, vertexStride, vertexOffset0);
            transformShaderNode .enableFloatAttrib (gl, "x3d_Vertex1", this .vertexBuffer, 4, vertexStride, vertexOffset1);
         }

         const numLines = this .getVertices () .length / 8;

         // Create lineTrianglesBuffer0

         if (this [_numLines0] !== numLines)
         {
            this [_numLines0]              = numLines;
            this [_lineTrianglesBuffer0] ??= gl .createBuffer ();

            gl .bindBuffer (gl .ARRAY_BUFFER, this [_lineTrianglesBuffer0]);
            gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ((16 * 6) * numLines), gl .DYNAMIC_DRAW);
         }

         // Transform lines.

         gl .bindBuffer (gl .ARRAY_BUFFER, null);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, browser .getLineTransformFeedback ());
         gl .bindBufferBase (gl .TRANSFORM_FEEDBACK_BUFFER, 0, this [_lineTrianglesBuffer0]);
         gl .enable (gl .RASTERIZER_DISCARD);
         gl .beginTransformFeedback (gl .POINTS);
         gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount / 2, 2);
         gl .endTransformFeedback ();
         gl .disable (gl .RASTERIZER_DISCARD);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, null);

         // DEBUG

         // const data = new Float32Array (16 * 6 * this .vertexCount / 2);
         // gl .bindBuffer (gl .ARRAY_BUFFER, this [_lineTrianglesBuffer0]);
         // gl .getBufferSubData (gl .ARRAY_BUFFER, 0, data);
         // console .log (data);

         // Pass 2

         if (this .hasNormals)
         {
            const transformShaderNode2 = browser .getLineTransformInstancedShader (2);

            transformShaderNode2 .enable (gl);

            // Setup vertex attributes.
            if (this .thickLinesVertexArrayObject .enable (transformShaderNode2 .getProgram ()))
            {
               if (this .hasTangents)
                  transformShaderNode2 .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

               if (this .hasTangents)
               {
                  const
                     tangentStride  = 8 * Float32Array .BYTES_PER_ELEMENT,
                     tangentOffset0 = 0,
                     tangentOffset1 = 4 * Float32Array .BYTES_PER_ELEMENT;

                  transformShaderNode2 .enableFloatAttrib (gl, "x3d_Tangent0", this .tangentBuffer, 4, tangentStride, tangentOffset0);
                  transformShaderNode2 .enableFloatAttrib (gl, "x3d_Tangent1", this .tangentBuffer, 4, tangentStride, tangentOffset1);
               }

               const
                  normalStride  = 6 * Float32Array .BYTES_PER_ELEMENT,
                  normalOffset0 = 0,
                  normalOffset1 = 3 * Float32Array .BYTES_PER_ELEMENT;

               transformShaderNode2 .enableFloatAttrib (gl, "x3d_Normal0", this .normalBuffer, 3, normalStride, normalOffset0);
               transformShaderNode2 .enableFloatAttrib (gl, "x3d_Normal1", this .normalBuffer, 3, normalStride, normalOffset1);
            }

            // Create lineTrianglesBuffer2

            if (this [_numLines2] !== numLines)
            {
               this [_numLines2]              = numLines;
               this .lineTrianglesBuffer2 ??= gl .createBuffer ();

               gl .bindBuffer (gl .ARRAY_BUFFER, this .lineTrianglesBuffer2);
               gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ((16 * 6) * numLines), gl .DYNAMIC_DRAW);
            }

            // Transform lines.

            gl .bindBuffer (gl .ARRAY_BUFFER, null);
            gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, browser .getLineTransformFeedback ());
            gl .bindBufferBase (gl .TRANSFORM_FEEDBACK_BUFFER, 0, this .lineTrianglesBuffer2);
            gl .enable (gl .RASTERIZER_DISCARD);
            gl .beginTransformFeedback (gl .POINTS);
            gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount / 2, 2);
            gl .endTransformFeedback ();
            gl .disable (gl .RASTERIZER_DISCARD);
            gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, null);

            // DEBUG

            // const data = new Float32Array (9 * 6 * 2);
            // gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer2]);
            // gl .getBufferSubData (gl .ARRAY_BUFFER, 0, data);
            // console .log (data);
         }

         // Set viewport.

         gl .viewport (... viewport);
         gl .scissor (... viewport);

         // Enable render mode nodes.

         for (const node of renderModeNodes)
            node .enable (gl);

         // Setup shader.

         shaderNode .enable (gl);
         shaderNode .setUniforms (gl, renderContext, this);

         // Setup vertex attributes.

         if (this .thickLinesVertexArrayObject .enable (shaderNode .getProgram ()))
         {
            if (this .hasNormals)
            {
               const
                  stride        = (9 + 4 + 3) * Float32Array .BYTES_PER_ELEMENT,
                  tangentOffset = 9 * Float32Array .BYTES_PER_ELEMENT,
                  normalOffset  = 13 * Float32Array .BYTES_PER_ELEMENT;

               if (this .hasTangents)
                  shaderNode .enableTangentAttribute (gl, this .lineTrianglesBuffer2, stride, tangentOffset);

               shaderNode .enableNormalAttribute (gl, this .lineTrianglesBuffer2, stride, normalOffset);
            }

            const
               stride            = 13 * Float32Array .BYTES_PER_ELEMENT,
               coordIndexOffset  = 0,
               lineStippleOffset = 1 * Uint32Array .BYTES_PER_ELEMENT,
               fogCoordOffset    = 4 * Float32Array .BYTES_PER_ELEMENT,
               colorOffset       = 5 * Float32Array .BYTES_PER_ELEMENT,
               vertexOffset      = 9 * Float32Array .BYTES_PER_ELEMENT;

            // for (let i = 0, length = attribNodes .length; i < length; ++ i)
            //    attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

            shaderNode .enableCoordIndexAttribute  (gl, this [_lineTrianglesBuffer0], stride, coordIndexOffset);
            shaderNode .enableLineStippleAttribute (gl, this [_lineTrianglesBuffer0], stride, lineStippleOffset);

            if (this .hasFogCoords)
               shaderNode .enableFogDepthAttribute (gl, this [_lineTrianglesBuffer0], stride, fogCoordOffset);

            if (this .colorMaterial)
               shaderNode .enableColorAttribute (gl, this [_lineTrianglesBuffer0], stride, colorOffset);

            shaderNode .enableVertexAttribute (gl, this [_lineTrianglesBuffer0], stride, vertexOffset);
         }

         gl .frontFace (gl .CCW);
         gl .enable (gl .CULL_FACE);
         gl .drawArrays (primitiveMode, 0, this .vertexCount * 3);

         // Disable render mode nodes.

         for (const node of renderModeNodes)
            node .disable (gl);

         // Reset texture units.

         browser .resetTextureUnits ();

         return true;
      };
   })(),
   displayInstanced (gl, renderContext, shapeNode)
   {
      if (this .displayInstancedThick (gl, renderContext, shapeNode))
         return;

      const
         { viewport, appearanceNode } = renderContext,
         browser         = this .getBrowser (),
         geometryContext = shapeNode .getGeometryContext (),
         shaderNode      = appearanceNode .getShader (geometryContext, renderContext),
         renderModeNodes = appearanceNode .getRenderModes (),
         primitiveMode   = browser .getPrimitiveMode (gl .LINES);

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Setup shader.

      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this);

      // Setup vertex attributes.

      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, particleOffset, velocityOffset, matrixOffset, normalMatrixOffset, colorOffset } = shapeNode;

         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (particleOffset !== undefined)
            shaderNode .enableParticleAttribute (gl, instances, instancesStride, particleOffset, 1);

         if (velocityOffset !== undefined)
            shaderNode .enableParticleVelocityAttribute (gl, instances, instancesStride, velocityOffset, 1);

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (geometryContext .colorMaterial)
         {
            if (geometryContext === this)
               shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);
            else
               shaderNode .enableColorAttribute (gl, instances, instancesStride, colorOffset, 1);
         }

         if (this .hasTangents)
            shaderNode .enableNormalAttribute (gl, this .tangentBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);

         this .updateInstances = false;
      }

      // Wireframes are always solid so only one drawing call is needed.

      gl .drawArraysInstanced (primitiveMode, 0, this .vertexCount, shapeNode .getNumInstances ());

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();

      gl .lineWidth (1);
   },
   displayInstancedThick: (() =>
   {
      const
         matrix                            = new Numbers_Matrix4 (),
         modelViewProjectionMatrixArray    = new Float32Array (16),
         invModelViewProjectionMatrixArray = new Float32Array (16);

      return function (gl, renderContext, shapeNode)
      {
         const
            { renderObject, viewport, appearanceNode } = renderContext,
            linePropertiesNode = appearanceNode .getStyleProperties (1);

         if (!linePropertiesNode)
            return false;

         // Also important for displayInstanced.
         if (linePropertiesNode .getApplied () && linePropertiesNode .getLinetype () !== 1)
            this .updateLengthSoFar (gl, renderContext);

         if (!linePropertiesNode .getTransformLines ())
            return false;

         const
            browser              = this .getBrowser (),
            geometryContext      = shapeNode .getGeometryContext (),
            shaderNode           = appearanceNode .getShader (geometryContext, renderContext),
            renderModeNodes      = appearanceNode .getRenderModes (),
            projectionMatrix     = renderObject .getProjectionMatrix () .get (),
            primitiveMode        = browser .getPrimitiveMode (gl .TRIANGLES),
            transformShaderNode0 = browser .getLineTransformInstancedShader (0);

         modelViewProjectionMatrixArray .set (matrix .assign (renderContext .modelViewMatrix) .multRight (projectionMatrix));
         invModelViewProjectionMatrixArray .set (matrix .inverse ());

         // Pass 0

         transformShaderNode0 .enable (gl);

         gl .uniform4f (transformShaderNode0 .viewport, ... viewport);
         gl .uniformMatrix4fv (transformShaderNode0 .modelViewProjectionMatrix,    false, modelViewProjectionMatrixArray);
         gl .uniformMatrix4fv (transformShaderNode0 .invModelViewProjectionMatrix, false, invModelViewProjectionMatrixArray);
         gl .uniform1f (transformShaderNode0 .linewidthScaleFactor1_2, linePropertiesNode .getLinewidthScaleFactor () / 2);

         // Setup vertex attributes.

         const instances = shapeNode .getInstances ();

         if (instances .thickLinesVertexArrayObject .update (this .updateInstances) .enable (transformShaderNode0 .getProgram ()))
         {
            const { instancesStride, matrixOffset, colorOffset } = shapeNode;

            transformShaderNode0 .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 2);

            const
               coordIndexStride  = 2 * Uint32Array .BYTES_PER_ELEMENT,
               coordIndexOffset0 = 0,
               coordIndexOffset1 = 1 * Uint32Array .BYTES_PER_ELEMENT,
               lengthSoFarStride = 6 * Float32Array .BYTES_PER_ELEMENT,
               lengthSoFarOffset = 5 * Float32Array .BYTES_PER_ELEMENT,
               fogDepthStride    = 2 * Float32Array .BYTES_PER_ELEMENT,
               fogDepthOffset0   = 0,
               fogDepthOffset1   = 1 * Float32Array .BYTES_PER_ELEMENT,
               colorStride       = 8 * Float32Array .BYTES_PER_ELEMENT,
               colorOffset0      = 0,
               colorOffset1      = 4 * Float32Array .BYTES_PER_ELEMENT,
               vertexStride      = 8 * Float32Array .BYTES_PER_ELEMENT,
               vertexOffset0     = 0,
               vertexOffset1     = 4 * Float32Array .BYTES_PER_ELEMENT;

            // for (let i = 0, length = attribNodes .length; i < length; ++ i)
            //    attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

            if (this .coordIndices .length)
            {
               transformShaderNode0 .enableUintAttrib (gl, "x3d_CoordIndex0", this .coordIndexBuffer, 1, coordIndexStride, coordIndexOffset0);
               transformShaderNode0 .enableUintAttrib (gl, "x3d_CoordIndex1", this .coordIndexBuffer, 1, coordIndexStride, coordIndexOffset1);
            }

            transformShaderNode0 .enableFloatAttrib (gl, "x3d_LengthSoFar", this .lineStippleBuffer, 1, lengthSoFarStride, lengthSoFarOffset);

            if (this .hasFogCoords)
            {
               transformShaderNode0 .enableFloatAttrib (gl, "x3d_FogDepth0", this .fogDepthBuffer, 1, fogDepthStride, fogDepthOffset0);
               transformShaderNode0 .enableFloatAttrib (gl, "x3d_FogDepth1", this .fogDepthBuffer, 1, fogDepthStride, fogDepthOffset1);
            }

            if (geometryContext .colorMaterial)
            {
               if (geometryContext === this)
               {
                  transformShaderNode0 .enableFloatAttrib (gl, "x3d_Color0", this .colorBuffer, 4, colorStride, colorOffset0);
                  transformShaderNode0 .enableFloatAttrib (gl, "x3d_Color1", this .colorBuffer, 4, colorStride, colorOffset1);
               }
               else
               {
                  transformShaderNode0 .enableFloatAttrib (gl, "x3d_Color0", instances, 4, instancesStride, colorOffset, 2);
                  transformShaderNode0 .enableFloatAttrib (gl, "x3d_Color1", instances, 4, instancesStride, colorOffset, 2);
               }
            }

            transformShaderNode0 .enableFloatAttrib (gl, "x3d_Vertex0", this .vertexBuffer, 4, vertexStride, vertexOffset0);
            transformShaderNode0 .enableFloatAttrib (gl, "x3d_Vertex1", this .vertexBuffer, 4, vertexStride, vertexOffset1);
         }

         // Create lineTrianglesBuffer0

         const numLines = this .getVertices () .length / 8 * shapeNode .getNumInstances ();

         if (shapeNode [_numLines0] !== numLines)
         {
            shapeNode [_numLines0]              = numLines;
            shapeNode [_lineTrianglesBuffer0] ??= gl .createBuffer ();

            gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer0]);
            gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ((13 * 6) * numLines), gl .DYNAMIC_DRAW);
         }

         // Transform lines.

         gl .bindBuffer (gl .ARRAY_BUFFER, null);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, browser .getLineTransformFeedback ());
         gl .bindBufferBase (gl .TRANSFORM_FEEDBACK_BUFFER, 0, shapeNode [_lineTrianglesBuffer0]);
         gl .enable (gl .RASTERIZER_DISCARD);
         gl .beginTransformFeedback (gl .POINTS);
         gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount / 2, 2 * shapeNode .getNumInstances ());
         gl .endTransformFeedback ();
         gl .disable (gl .RASTERIZER_DISCARD);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, null);

         // DEBUG

         // const data = new Float32Array (16 * 6 * this .vertexCount / 2);
         // gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer0]);
         // gl .getBufferSubData (gl .ARRAY_BUFFER, 0, data);
         // console .log (data);

         // Pass 1

         const transformShaderNode1 = browser .getLineTransformInstancedShader (1);

         transformShaderNode1 .enable (gl);

         // Setup vertex attributes.

         if (instances .thickLinesVertexArrayObject .update (this .updateInstances) .enable (transformShaderNode1 .getProgram ()))
         {
            const { instancesStride, matrixOffset } = shapeNode;

            transformShaderNode1 .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);
         }

         // Create lineTrianglesBuffer1

         if (shapeNode [_numLines1] !== numLines)
         {
            shapeNode [_numLines1]              = numLines;
            shapeNode [_lineTrianglesBuffer1] ??= gl .createBuffer ();

            gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer1]);
            gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ((16 * 6) * numLines), gl .DYNAMIC_DRAW);
         }

         // Transform lines.

         gl .bindBuffer (gl .ARRAY_BUFFER, null);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, browser .getLineTransformFeedback ());
         gl .bindBufferBase (gl .TRANSFORM_FEEDBACK_BUFFER, 0, shapeNode [_lineTrianglesBuffer1]);
         gl .enable (gl .RASTERIZER_DISCARD);
         gl .beginTransformFeedback (gl .POINTS);
         gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount, shapeNode .getNumInstances ());
         gl .endTransformFeedback ();
         gl .disable (gl .RASTERIZER_DISCARD);
         gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, null);

         // DEBUG

         // const data = new Float32Array (16 * 6 * 2);
         // gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer1]);
         // gl .getBufferSubData (gl .ARRAY_BUFFER, 0, data);
         // console .log (data);

         // Pass 2

         if (this .hasNormals)
         {
            const transformShaderNode2 = browser .getLineTransformInstancedShader (2);

            transformShaderNode2 .enable (gl);

            // Setup vertex attributes.

            if (instances .thickLinesVertexArrayObject .update (this .updateInstances) .enable (transformShaderNode2 .getProgram ()))
            {
               const { instancesStride, normalMatrixOffset } = shapeNode;

               transformShaderNode2 .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 2);

               if (this .hasTangents)
                  transformShaderNode2 .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

               if (this .hasTangents)
               {
                  const
                     tangentStride  = 8 * Float32Array .BYTES_PER_ELEMENT,
                     tangentOffset0 = 0,
                     tangentOffset1 = 4 * Float32Array .BYTES_PER_ELEMENT;

                  transformShaderNode2 .enableFloatAttrib (gl, "x3d_Tangent0", this .tangentBuffer, 4, tangentStride, tangentOffset0);
                  transformShaderNode2 .enableFloatAttrib (gl, "x3d_Tangent1", this .tangentBuffer, 4, tangentStride, tangentOffset1);
               }

               const
                  normalStride  = 6 * Float32Array .BYTES_PER_ELEMENT,
                  normalOffset0 = 0,
                  normalOffset1 = 3 * Float32Array .BYTES_PER_ELEMENT;

               transformShaderNode2 .enableFloatAttrib (gl, "x3d_Normal0", this .normalBuffer, 3, normalStride, normalOffset0);
               transformShaderNode2 .enableFloatAttrib (gl, "x3d_Normal1", this .normalBuffer, 3, normalStride, normalOffset1);
            }

            // Create lineTrianglesBuffer2

            if (shapeNode [_numLines2] !== numLines)
            {
               shapeNode [_numLines2]              = numLines;
               shapeNode [_lineTrianglesBuffer2] ??= gl .createBuffer ();

               gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer2]);
               gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ((16 * 6) * numLines), gl .DYNAMIC_DRAW);
            }

            // Transform lines.

            gl .bindBuffer (gl .ARRAY_BUFFER, null);
            gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, browser .getLineTransformFeedback ());
            gl .bindBufferBase (gl .TRANSFORM_FEEDBACK_BUFFER, 0, shapeNode [_lineTrianglesBuffer2]);
            gl .enable (gl .RASTERIZER_DISCARD);
            gl .beginTransformFeedback (gl .POINTS);
            gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount / 2, 2 * shapeNode .getNumInstances ());
            gl .endTransformFeedback ();
            gl .disable (gl .RASTERIZER_DISCARD);
            gl .bindTransformFeedback (gl .TRANSFORM_FEEDBACK, null);

            // DEBUG

            // const data = new Float32Array (9 * 6 * 2);
            // gl .bindBuffer (gl .ARRAY_BUFFER, shapeNode [_lineTrianglesBuffer2]);
            // gl .getBufferSubData (gl .ARRAY_BUFFER, 0, data);
            // console .log (data);
         }

         // Set viewport.

         gl .viewport (... viewport);
         gl .scissor (... viewport);

         // Enable render mode nodes.

         for (const node of renderModeNodes)
            node .enable (gl);

         // Setup shader.

         shaderNode .enable (gl);
         shaderNode .setUniforms (gl, renderContext, geometryContext);

         // Setup vertex attributes.

         if (instances .thickLinesVertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
         {
            shaderNode .enableInstanceMatrixAttribute (gl, shapeNode [_lineTrianglesBuffer1], 0, 0, 0);

            if (this .hasNormals)
            {
               const
                  stride             = (9 + 4 + 3) * Float32Array .BYTES_PER_ELEMENT,
                  normalMatrixOffset = 0,
                  tangentOffset      = 9 * Float32Array .BYTES_PER_ELEMENT,
                  normalOffset       = 13 * Float32Array .BYTES_PER_ELEMENT;

               if (shapeNode .normalMatrixOffset)
                  shaderNode .enableInstanceNormalMatrixAttribute (gl, shapeNode [_lineTrianglesBuffer2], stride, normalMatrixOffset, 0);

               if (this .hasTangents)
                  shaderNode .enableTangentAttribute (gl, shapeNode [_lineTrianglesBuffer2], stride, tangentOffset);

               shaderNode .enableNormalAttribute (gl, shapeNode [_lineTrianglesBuffer2], stride, normalOffset);
            }

            const
               stride            = 13 * Float32Array .BYTES_PER_ELEMENT,
               coordIndexOffset  = 0,
               lineStippleOffset = 1 * Uint32Array .BYTES_PER_ELEMENT,
               fogCoordOffset    = 4 * Float32Array .BYTES_PER_ELEMENT,
               colorOffset       = 5 * Float32Array .BYTES_PER_ELEMENT,
               vertexOffset      = 9 * Float32Array .BYTES_PER_ELEMENT;

            // for (let i = 0, length = attribNodes .length; i < length; ++ i)
            //    attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

            shaderNode .enableCoordIndexAttribute  (gl, shapeNode [_lineTrianglesBuffer0], stride, coordIndexOffset);
            shaderNode .enableLineStippleAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, lineStippleOffset);

            if (this .hasFogCoords)
               shaderNode .enableFogDepthAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, fogCoordOffset);

            if (geometryContext .colorMaterial)
               shaderNode .enableColorAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, colorOffset);

            shaderNode .enableVertexAttribute (gl, shapeNode [_lineTrianglesBuffer0], stride, vertexOffset);

            this .updateInstances = false;
         }

         gl .frontFace (gl .CCW);
         gl .enable (gl .CULL_FACE);
         gl .drawArrays (primitiveMode, 0, this .vertexCount * 3 * shapeNode .getNumInstances ());

         // Disable render mode nodes.

         for (const node of renderModeNodes)
            node .disable (gl);

         // Reset texture units.

         browser .resetTextureUnits ();

         return true;
      };
   })(),
});

Object .defineProperties (X3DLineGeometryNode, Core_X3DNode .getStaticProperties ("X3DLineGeometryNode", "Rendering", 1));

const X3DLineGeometryNode_default_ = X3DLineGeometryNode;
;

/* harmony default export */ const Rendering_X3DLineGeometryNode = (Namespace/* default */.A .add ("X3DLineGeometryNode", X3DLineGeometryNode_default_));
;// ./src/x_ite/Components/Rendering/IndexedLineSet.js







function IndexedLineSet (executionContext)
{
   Rendering_X3DLineGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .IndexedLineSet);
}

Object .assign (Object .setPrototypeOf (IndexedLineSet .prototype, Rendering_X3DLineGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DLineGeometryNode .prototype .initialize .call (this);

      this ._set_colorIndex .addFieldInterest (this ._colorIndex);
      this ._set_coordIndex .addFieldInterest (this ._coordIndex);

      this ._attrib   .addInterest ("set_attrib__",   this);
      this ._fogCoord .addInterest ("set_fogCoord__", this);
      this ._color    .addInterest ("set_color__",    this);
      this ._tangent  .addInterest ("set_tangent__",  this);
      this ._normal   .addInterest ("set_normal__",   this);
      this ._coord    .addInterest ("set_coord__",    this);

      this .set_attrib__ ();
      this .set_fogCoord__ ();
      this .set_color__ ();
      this .set_tangent__ ();
      this .set_normal__ ();
      this .set_coord__ ();
   },
   getColorPerVertexIndex (index)
   {
      if (index < this ._colorIndex .length)
         return this ._colorIndex [index];

      return this ._coordIndex [index];
   },
   getColorIndex (index)
   {
      if (index < this ._colorIndex .length)
         return this ._colorIndex [index];

      return index;
   },
   getPolylineIndices ()
   {
      const
         coordIndex = this ._coordIndex,
         polylines  = [ ];

      let polyline = [ ];

      if (coordIndex .length)
      {
         for (let i = 0, length = coordIndex .length; i < length; ++ i)
         {
            const index = coordIndex [i];

            if (index >= 0)
               // Add vertex.
               polyline .push (i);

            else
            {
               // Negativ index.
               // Add polylines.
               polylines .push (polyline);

               polyline = [ ];
            }
         }

         if (coordIndex [coordIndex .length - 1] >= 0)
         {
            polylines .push (polyline);
         }
      }

      return polylines;
   },
   build ()
   {
      if (!this .coordNode ?.getSize ())
         return;

      const
         coordIndex        = this ._coordIndex,
         polylines         = this .getPolylineIndices (),
         colorPerVertex    = this ._colorPerVertex .getValue (),
         coordIndicesArray = this .getCoordIndices (),
         attribNodes       = this .getAttrib (),
         numAttribNodes    = attribNodes .length,
         attribArrays      = this .getAttribs (),
         fogCoordNode      = this .getFogCoord (),
         colorNode         = this .getColor (),
         tangentNode       = this .getTangent (),
         normalNode        = this .getNormal (),
         coordNode         = this .getCoord (),
         fogDepthArray     = this .getFogDepths (),
         colorArray        = this .getColors (),
         tangentArray      = this .getTangents (),
         normalArray       = this .getNormals (),
         vertexArray       = this .getVertices ();

      // Fill GeometryNode

      let face = 0;

      for (const polyline of polylines)
      {
         // Create two vertices for each line.

         if (polyline .length > 1)
         {
            for (let line = 0, l_end = polyline .length - 1; line < l_end; ++ line)
            {
               for (let l = line, i_end = line + 2; l < i_end; ++ l)
               {
                  const
                     i     = polyline [l],
                     index = coordIndex [i];

                  coordIndicesArray .push (index);

                  for (let a = 0; a < numAttribNodes; ++ a)
                     attribNodes [a] .addValue (index, attribArrays [a]);

                  fogCoordNode ?.addDepth (index, fogDepthArray);

                  colorNode ?.addColor (colorPerVertex ? this .getColorPerVertexIndex (i) : this .getColorIndex (face), colorArray);

                  tangentNode ?.addVector (index, tangentArray);
                  normalNode  ?.addVector (index, normalArray);

                  coordNode .addPoint (index, vertexArray);
               }
            }
         }

         ++ face;
      }
   },
});

Object .defineProperties (IndexedLineSet,
{
   ... Core_X3DNode .getStaticProperties ("IndexedLineSet", "Rendering", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_colorIndex", new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_coordIndex", new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorIndex",     new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "coordIndex",     new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",         new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",        new x_ite_Fields .SFNode ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",          new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const IndexedLineSet_default_ = IndexedLineSet;
;

/* harmony default export */ const Rendering_IndexedLineSet = (Namespace/* default */.A .add ("IndexedLineSet", IndexedLineSet_default_));
;// ./src/x_ite/Browser/Grouping/X3DGroupingContext.js




const _bboxShape = Symbol ();

function X3DGroupingContext () { }

Object .assign (X3DGroupingContext .prototype,
{
   getBBoxShape ()
   {
      return this [_bboxShape] ??= (() =>
      {
         const
            bboxShape      = new Shape_Shape (this .getPrivateScene ()),
            bboxGeometry   = new Rendering_IndexedLineSet (this .getPrivateScene ()),
            bboxCoordinate = new Rendering_Coordinate (this .getPrivateScene ());

         bboxShape ._pointerEvents = false;
         bboxShape ._castShadow    = false;
         bboxShape ._geometry      = bboxGeometry;
         bboxGeometry ._coordIndex = [0, 1, 2, 3, 0, -1, 4, 5, 6, 7, 4, -1, 0, 4, -1, 1, 5, -1, 2, 6, -1, 3, 7, -1];
         bboxGeometry ._coord      = bboxCoordinate;
         bboxCoordinate ._point    = [0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5];

         bboxCoordinate .setPrivate (true);
         bboxGeometry   .setPrivate (true);
         bboxShape      .setPrivate (true);

         bboxCoordinate .setup ();
         bboxGeometry   .setup ();
         bboxShape      .setup ();

         return bboxShape;
      })();
   },
});

const X3DGroupingContext_default_ = X3DGroupingContext;
;

/* harmony default export */ const Grouping_X3DGroupingContext = (Namespace/* default */.A .add ("X3DGroupingContext", X3DGroupingContext_default_));
;// ./src/x_ite/Components/Layering/X3DViewportNode.js




function X3DViewportNode (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DViewportNode);
}

Object .setPrototypeOf (X3DViewportNode .prototype, Grouping_X3DGroupingNode .prototype);

Object .defineProperties (X3DViewportNode, Core_X3DNode .getStaticProperties ("X3DViewportNode", "Layering", 1));

const X3DViewportNode_default_ = X3DViewportNode;
;

/* harmony default export */ const Layering_X3DViewportNode = (Namespace/* default */.A .add ("X3DViewportNode", X3DViewportNode_default_));
;// ./src/x_ite/Components/Layering/Viewport.js











function Viewport (executionContext)
{
   Layering_X3DViewportNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Viewport);

   this .viewVolume = new Geometry_ViewVolume ();
   this .rectangle  = new Numbers_Vector4 ();
}

Object .assign (Object .setPrototypeOf (Viewport .prototype, Layering_X3DViewportNode .prototype),
{
   initialize ()
   {
      Layering_X3DViewportNode .prototype .initialize .call (this);

      this ._clipBoundary .addInterest ("set_clipBoundary__", this);

      this .set_clipBoundary__ ();
   },
   set_clipBoundary__ ()
   {
      this .left   = Math_Algorithm .clamp (this ._clipBoundary [0] ?? 0, 0, 1);
      this .right  = Math_Algorithm .clamp (this ._clipBoundary [1] ?? 1, 0, 1);
      this .bottom = Math_Algorithm .clamp (this ._clipBoundary [2] ?? 0, 0, 1);
      this .top    = Math_Algorithm .clamp (this ._clipBoundary [3] ?? 1, 0, 1);
   },
   getRectangle (viewport = this .getBrowser () .getViewport ())
   {
      const
         left   = Math .floor (viewport [0] + viewport [2] * this .left),
         right  = Math .floor (viewport [0] + viewport [2] * this .right),
         bottom = Math .floor (viewport [1] + viewport [3] * this .bottom),
         top    = Math .floor (viewport [1] + viewport [3] * this .top);

      this .rectangle .set (left,
                            bottom,
                            Math .max (right - left, 0),
                            Math .max (top - bottom, 0));

      return this .rectangle;
   },
   traverse (type, renderObject)
   {
      try
      {
         this .push (renderObject);

         switch (type)
         {
            case Rendering_TraverseType .POINTER:
            {
               const browser = this .getBrowser ();

               if (!browser .getPointingLayer ())
               {
                  if (!browser .isPointerInRectangle (this .rectangle))
                     return;
               }

               // falls through
            }
            default:
            {
               Layering_X3DViewportNode .prototype .traverse .call (this, type, renderObject);
               break;
            }
         }
      }
      finally
      {
         this .pop (renderObject);
      }
   },
   push (renderObject)
   {
      const
         viewVolumes = renderObject .getViewVolumes (),
         viewVolume  = this .viewVolume,
         rectangle   = this .getRectangle (viewVolumes .at (-1) ?.getViewport ());

      viewVolume .set (renderObject .getProjectionMatrix () .get (), rectangle);
      viewVolumes .push (viewVolume);
   },
   pop (renderObject)
   {
      renderObject .getViewVolumes () .pop ();
   },
});

Object .defineProperties (Viewport,
{
   ... Core_X3DNode .getStaticProperties ("Viewport", "Layering", 1, "viewport", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "clipBoundary",   new x_ite_Fields .MFFloat (0, 1, 0, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",       new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Viewport_default_ = Viewport;
;

/* harmony default export */ const Layering_Viewport = (Namespace/* default */.A .add ("Viewport", Viewport_default_));
;// ./src/x_ite/Browser/Layering/X3DLayeringContext.js


const _defaultViewport = Symbol ();

function X3DLayeringContext ()
{
   this [_defaultViewport] = new Layering_Viewport (this .getPrivateScene ());
}

Object .assign (X3DLayeringContext .prototype,
{
   initialize ()
   {
      this [_defaultViewport] .setup ();
   },
   getDefaultViewport ()
   {
      return this [_defaultViewport];
   },
});

const X3DLayeringContext_default_ = X3DLayeringContext;
;

/* harmony default export */ const Layering_X3DLayeringContext = (Namespace/* default */.A .add ("X3DLayeringContext", X3DLayeringContext_default_));
;// ./src/assets/shaders/webgl2/pbr/ToneMapping2.glsl.js
const ToneMapping2_glsl_default_ = () => /* glsl */ `
const float GAMMA=2.2;const float INV_GAMMA=1./GAMMA;vec3 linearToSRGB(const in vec3 color){return pow(color,vec3(INV_GAMMA));}vec4 linearToSRGB(const in vec4 color){return vec4(linearToSRGB(color.rgb),color.a);}vec3 sRGBToLinear(const in vec3 color){return pow(color,vec3(GAMMA));}vec4 sRGBToLinear(const in vec4 color){return vec4(sRGBToLinear(color.rgb),color.a);}
#if defined(X3D_LINEAR_OUTPUT)
vec3 toneMap(in vec3 color){
#if defined(X3D_COLORSPACE_SRGB)
color=sRGBToLinear(color);
#endif
return color;}
#else
#if defined(X3D_TONEMAP_ACES_NARKOWICZ)
vec3 toneMapACES_Narkowicz(const in vec3 color){const float A=2.51;const float B=.03;const float C=2.43;const float D=.59;const float E=.14;return clamp((color*(A*color+B))/(color*(C*color+D)+E),0.,1.);}
#endif
#if defined(X3D_TONEMAP_ACES_HILL)||defined(X3D_TONEMAP_ACES_HILL_EXPOSURE_BOOST)
const mat3 ACESInputMat=mat3(.59719,.07600,.02840,.35458,.90834,.13383,.04823,.01566,.83777);const mat3 ACESOutputMat=mat3(1.60475,-.10208,-.00327,-.53108,1.10813,-.07276,-.07367,-.00605,1.07602);vec3 RRTAndODTFit(const in vec3 color){vec3 a=color*(color+.0245786)-.000090537;vec3 b=color*(.983729*color+.4329510)+.238081;return a/b;}vec3 toneMapACES_Hill(in vec3 color){color=ACESInputMat*color;color=RRTAndODTFit(color);color=ACESOutputMat*color;color=clamp(color,0.,1.);return color;}
#endif
#if defined(X3D_TONEMAP_KHR_PBR_NEUTRAL)
vec3 toneMap_KhronosPbrNeutral(in vec3 color){const float startCompression=.8-.04;const float desaturation=.15;float x=min(color.r,min(color.g,color.b));float offset=x<.08?x-6.25*x*x:.04;color-=offset;float peak=max(color.r,max(color.g,color.b));if(peak<startCompression)return color;const float d=1.-startCompression;float newPeak=1.-d*d/(peak+d-startCompression);color*=newPeak/peak;float g=1.-1./(desaturation*(peak-newPeak)+1.);return mix(color,newPeak*vec3(1),g);}
#endif
uniform float x3d_Exposure;vec3 toneMap(in vec3 color){color*=x3d_Exposure;
#if defined(X3D_TONEMAP_ACES_NARKOWICZ)
color=toneMapACES_Narkowicz(color);
#endif
#if defined(X3D_TONEMAP_ACES_HILL)
color=toneMapACES_Hill(color);
#endif
#if defined(X3D_TONEMAP_ACES_HILL_EXPOSURE_BOOST)
color/=.6;color=toneMapACES_Hill(color);
#endif
#if defined(X3D_TONEMAP_KHR_PBR_NEUTRAL)
color=toneMap_KhronosPbrNeutral(color);
#endif
#if defined(X3D_COLORSPACE_LINEAR)
color=linearToSRGB(color);
#endif
return color;}
#endif
`
;

/* harmony default export */ const ToneMapping2_glsl = (Namespace/* default */.A .add ("ToneMapping2.glsl", ToneMapping2_glsl_default_));
;// ./src/x_ite/Browser/Lighting/Distribution.js
let Distribution_i = 0;

const Distribution =
{
   LAMBERTIAN: Distribution_i ++,
   GGX:        Distribution_i ++,
   CHARLIE:    Distribution_i ++,
};

const Distribution_default_ = Distribution;
;

/* harmony default export */ const Lighting_Distribution = (Namespace/* default */.A .add ("Distribution", Distribution_default_));
;// ./src/x_ite/Browser/Lighting/Filter2.fs.js



const Filter2_fs_default_ = /* glsl */ `#version 300 es
precision highp float;precision highp int;precision highp samplerCube;
${ToneMapping2_glsl ()}
${Object .entries (Lighting_Distribution) .map (([name, value]) => `#define X3D_${name} ${value}`) .join ("\n")}
const float M_PI=3.1415926535897932384626433832795;uniform samplerCube x3d_TextureEXT;uniform int x3d_TextureSizeEXT;uniform bool x3d_TextureLinearEXT;uniform int x3d_CurrentFaceEXT;uniform int x3d_DistributionEXT;uniform int x3d_SampleCountEXT;uniform float x3d_RoughnessEXT;uniform float x3d_LodBiasEXT;uniform float x3d_IntensityEXT;uniform vec3 x3d_FlipEXT;in vec2 texCoord;out vec4 x3d_FragColor;mat3 generateTBN(const in vec3 normal){vec3 bitangent=vec3(0.,1.,0.);float NdotUp=dot(normal,vec3(0.,1.,0.));float epsilon=.0000001;if(1.-abs(NdotUp)<=epsilon){if(NdotUp>0.)bitangent=vec3(0.,0.,1.);else bitangent=vec3(0.,0.,-1.);}vec3 tangent=normalize(cross(bitangent,normal));bitangent=cross(normal,tangent);return mat3(tangent,bitangent,normal);}struct MicrofacetDistributionSample{float pdf;float cosTheta;float sinTheta;float phi;};float radicalInverse_VdC(in uint bits){bits=(bits<<16u)|(bits>>16u);bits=((bits&0x55555555u)<<1u)|((bits&0xAAAAAAAAu)>>1u);bits=((bits&0x33333333u)<<2u)|((bits&0xCCCCCCCCu)>>2u);bits=((bits&0x0F0F0F0Fu)<<4u)|((bits&0xF0F0F0F0u)>>4u);bits=((bits&0x00FF00FFu)<<8u)|((bits&0xFF00FF00u)>>8u);return float(bits)*2.3283064365386963e-10;}vec2 hammersley2d(const in int i,const in int N){return vec2(float(i)/float(N),radicalInverse_VdC(uint(i)));}MicrofacetDistributionSample Lambertian(const in vec2 xi,const in float roughness){MicrofacetDistributionSample lambertian;lambertian.cosTheta=sqrt(1.-xi.y);lambertian.sinTheta=sqrt(xi.y);lambertian.phi=2.*M_PI*xi.x;lambertian.pdf=lambertian.cosTheta/M_PI;return lambertian;}float saturate(const in float v){return clamp(v,0.,1.);}float D_GGX(const in float NdotH,const in float roughness){float a=NdotH*roughness;float k=roughness/(1.-NdotH*NdotH+a*a);return k*k*(1./M_PI);}MicrofacetDistributionSample GGX(const in vec2 xi,const in float roughness){MicrofacetDistributionSample ggx;float alpha=roughness*roughness;ggx.cosTheta=saturate(sqrt((1.-xi.y)/(1.+(alpha*alpha-1.)*xi.y)));ggx.sinTheta=sqrt(1.-ggx.cosTheta*ggx.cosTheta);ggx.phi=2.*M_PI*xi.x;ggx.pdf=D_GGX(ggx.cosTheta,alpha);ggx.pdf/=4.;return ggx;}float D_Charlie(in float sheenRoughness,const in float NdotH){sheenRoughness=max(sheenRoughness,.000001);float invR=1./sheenRoughness;float cos2h=NdotH*NdotH;float sin2h=1.-cos2h;return(2.+invR)*pow(sin2h,invR*.5)/(2.*M_PI);}MicrofacetDistributionSample Charlie(const in vec2 xi,const in float roughness){MicrofacetDistributionSample charlie;float alpha=roughness*roughness;charlie.sinTheta=pow(xi.y,alpha/(2.*alpha+1.));charlie.cosTheta=sqrt(1.-charlie.sinTheta*charlie.sinTheta);charlie.phi=2.*M_PI*xi.x;charlie.pdf=D_Charlie(alpha,charlie.cosTheta);charlie.pdf/=4.;return charlie;}vec4 getImportanceSample(const in int sampleIndex,const in vec3 N,const in float roughness){vec2 xi=hammersley2d(sampleIndex,x3d_SampleCountEXT);MicrofacetDistributionSample importanceSample;switch(x3d_DistributionEXT){case X3D_LAMBERTIAN:{importanceSample=Lambertian(xi,roughness);break;}case X3D_GGX:{importanceSample=GGX(xi,roughness);break;}case X3D_CHARLIE:{importanceSample=Charlie(xi,roughness);break;}}vec3 localSpaceDirection=normalize(vec3(importanceSample.sinTheta*cos(importanceSample.phi),importanceSample.sinTheta*sin(importanceSample.phi),importanceSample.cosTheta));mat3 TBN=generateTBN(N);vec3 direction=TBN*localSpaceDirection;return vec4(direction,importanceSample.pdf);}float computeLod(const in float pdf){float lod=.5*log2(6.*float(x3d_TextureSizeEXT)*float(x3d_TextureSizeEXT)/(float(x3d_SampleCountEXT)*pdf));return lod;}vec3 filterColor(const in vec3 N){vec3 color=vec3(0);float weight=0.;for(int i=0;i<x3d_SampleCountEXT;++i){vec4 importanceSample=getImportanceSample(i,N,x3d_RoughnessEXT);vec3 H=importanceSample.xyz;float pdf=importanceSample.w;float lod=computeLod(pdf);lod+=x3d_LodBiasEXT;switch(x3d_DistributionEXT){case X3D_LAMBERTIAN:{vec3 lambertian=textureLod(x3d_TextureEXT,H,lod).rgb;if(!x3d_TextureLinearEXT)lambertian=sRGBToLinear(lambertian);color+=lambertian;break;}case X3D_GGX:case X3D_CHARLIE:{vec3 V=N;vec3 L=normalize(reflect(-V,H));float NdotL=dot(N,L);if(NdotL>0.){if(x3d_RoughnessEXT==0.){lod=x3d_LodBiasEXT;}vec3 sampleColor=textureLod(x3d_TextureEXT,L,lod).rgb;if(!x3d_TextureLinearEXT)sampleColor=sRGBToLinear(sampleColor);color+=sampleColor*NdotL;weight+=NdotL;}break;}}}if(weight!=0.0f)color/=weight;else color/=float(x3d_SampleCountEXT);return color*x3d_IntensityEXT;}vec3 getNormal(const in int face,const in vec2 t){float x;float y;float z;switch(face){case 0:x=t.x;y=-t.y;z=1.;break;case 1:x=-t.x;y=-t.y;z=-1.;break;case 2:x=-1.;y=-t.y;z=t.x;break;case 3:x=1.;y=-t.y;z=-t.x;break;case 4:x=t.x;y=1.;z=t.y;break;case 5:x=t.x;y=-1.;z=-t.y;break;}return normalize(vec3(x,y,z))*x3d_FlipEXT;}void main(){vec3 normal=getNormal(x3d_CurrentFaceEXT,texCoord);x3d_FragColor=vec4(filterColor(normal),1.);}`
;

/* harmony default export */ const Filter2_fs = (Namespace/* default */.A .add ("Filter2.fs", Filter2_fs_default_));
;// ./src/x_ite/Browser/Lighting/X3DLightingContext.js






const
   _maxLights                = Symbol (),
   _textures                 = Symbol (),
   _shadowBuffers            = Symbol (),
   _environmentTextureShader = Symbol (),
   _filterFrameBuffer        = Symbol ();

function X3DLightingContext ()
{
   const
      gl                   = this .getContext (),
      maxTextureImageUnits = gl .getParameter (gl .MAX_TEXTURE_IMAGE_UNITS);

   if (maxTextureImageUnits > 8)
      this [_maxLights] = 8;
   else
      this [_maxLights] = 2;

   this [_textures]      = new Map ();
   this [_shadowBuffers] = [ ]; // Shadow buffer cache
}

Object .assign (X3DLightingContext .prototype,
{
   getMaxEnvironmentLights ()
   {
      return 1;
   },
   getMaxLights ()
   {
      return this [_maxLights];
   },
   getLibraryTexture (name)
   {
      return this [_textures] .get (name) ?? this .createLibraryTexture (name);
   },
   createLibraryTexture (name)
   {
      const
         texture           = new Texturing_ImageTexture (this .getPrivateScene ()),
         textureProperties = new Texturing_TextureProperties (this .getPrivateScene ());

      textureProperties ._generateMipMaps     = false;
      textureProperties ._minificationFilter  = "AVG_PIXEL";
      textureProperties ._magnificationFilter = "AVG_PIXEL";
      textureProperties ._boundaryModeS       = "CLAMP_TO_BOUNDARY";
      textureProperties ._boundaryModeT       = "CLAMP_TO_BOUNDARY";
      textureProperties ._boundaryModeR       = "CLAMP_TO_BOUNDARY";

      textureProperties .setup ();

      texture ._url               = [Networking_URLs .getLibraryURL (name)];
      texture ._textureProperties = textureProperties;

      texture .setup ();

      this [_textures] .set (name, texture)

      return texture;
   },
   popShadowBuffer (shadowMapSize)
   {
      try
      {
         const shadowBuffers = this [_shadowBuffers] [shadowMapSize] ??= [ ];

         if (shadowBuffers .length)
            return shadowBuffers .pop ();

         return new Rendering_TextureBuffer ({
            browser: this,
            width: shadowMapSize,
            height: shadowMapSize,
            float: true,
         });
      }
      catch (error)
      {
         // Couldn't create texture buffer.
         console .error (error);

         return null;
      }
   },
   pushShadowBuffer (buffer)
   {
      if (buffer)
         this [_shadowBuffers] [buffer .getWidth ()] .push (buffer);
   },
   getEnvironmentTextureShader ()
   {
      return this [_environmentTextureShader] ??= this .createShader ({
         name: "EnvironmentTexture",
         vertexShader: "FullScreen",
         fragmentShader:`data:x-shader/x-fragment,${Filter2_fs}`,
         uniforms: [
            "x3d_TextureEXT",
            "x3d_TextureSizeEXT",
            "x3d_TextureLinearEXT",
            "x3d_CurrentFaceEXT",
            "x3d_DistributionEXT",
            "x3d_SampleCountEXT",
            "x3d_RoughnessEXT",
            "x3d_LodBiasEXT",
            "x3d_IntensityEXT",
            "x3d_FlipEXT",
         ],
      });
   },
   filterEnvironmentTexture ({ name, texture, distribution, sampleCount, roughness, flipX, cachedNode })
   {
      // Render the texture.

      const
         gl                 = this .getContext (),
         executionContext   = texture .getExecutionContext (),
         currentFramebuffer = gl .getParameter (gl .FRAMEBUFFER_BINDING),
         currentProgram     = gl .getParameter (gl .CURRENT_PROGRAM),
         shaderNode         = this .getEnvironmentTextureShader (),
         framebuffer        = this [_filterFrameBuffer] ??= gl .createFramebuffer (),
         size               = texture .getSize (),
         filtered           = cachedNode ?? executionContext .createNode ("ImageCubeMapTexture", false);

      // Setup texture.

      if (!cachedNode)
      {
         filtered .setName (name);
         filtered .setPrivate (true);
         filtered .setLinear (true);
         filtered .setup ();
      }

      filtered .setSize (size);

      // Resize texture.

      const data = new Uint8Array (size * size * 4);

      gl .bindTexture (filtered .getTarget (), filtered .getTexture ());

      for (const target of filtered .getTargets ())
         gl .texImage2D (target, 0, gl .RGBA, size, size, 0, gl .RGBA, gl .UNSIGNED_BYTE, data);

      if (roughness .length > 1)
      {
         gl .generateMipmap (gl .TEXTURE_CUBE_MAP);
         gl .texParameteri (gl .TEXTURE_CUBE_MAP, gl .TEXTURE_MIN_FILTER, gl .LINEAR_MIPMAP_LINEAR);
         gl .texParameteri (gl .TEXTURE_CUBE_MAP, gl .TEXTURE_MAG_FILTER, gl .LINEAR);
      }

      // Setup defaults.

      gl .bindFramebuffer (gl .FRAMEBUFFER, framebuffer);
      gl .disable (gl .DEPTH_TEST);
      gl .enable (gl .CULL_FACE);
      gl .frontFace (gl .CCW);
      gl .clearColor (0, 0, 0, 0);
      gl .bindVertexArray (this .getFullscreenVertexArrayObject ());

      // Setup specular texture uniforms.

      gl .useProgram (shaderNode .getProgram ());
      gl .activeTexture (gl .TEXTURE0);
      gl .bindTexture (gl .TEXTURE_CUBE_MAP, texture .getTexture ());
      gl .uniform1i (shaderNode .x3d_TextureEXT, 0);
      gl .uniform1i (shaderNode .x3d_TextureSizeEXT, size);
      gl .uniform1i (shaderNode .x3d_TextureLinearEXT, texture .isLinear ());
      gl .uniform1i (shaderNode .x3d_DistributionEXT, distribution);
      gl .uniform1i (shaderNode .x3d_SampleCountEXT, sampleCount);
      gl .uniform1f (shaderNode .x3d_LodBiasEXT, 0);
      gl .uniform1f (shaderNode .x3d_IntensityEXT, 1);
      gl .uniform3f (shaderNode .x3d_FlipEXT, flipX ? -1 : 1, 1, 1);

      for (const [level, r] of roughness .entries ())
      {
         const mipSize = size >> level;

         // console .log (level, mipSize, r, filtered .getLevels ());

         // Setup mip level uniforms.

         gl .uniform1f (shaderNode .x3d_RoughnessEXT, r);

         // Generate images.

         gl .viewport (0, 0, mipSize, mipSize);
         gl .scissor  (0, 0, mipSize, mipSize);

         for (let i = 0; i < 6; ++ i)
         {
            gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, filtered .getTargets () [i], filtered .getTexture (), level);

            gl .clear (gl .COLOR_BUFFER_BIT);
            gl .uniform1i (shaderNode .x3d_CurrentFaceEXT, i);
            gl .drawArrays (gl .TRIANGLES, 0, 6);
         }
      }

      gl .enable (gl .DEPTH_TEST);
      gl .useProgram (currentProgram);
      gl .bindFramebuffer (gl .FRAMEBUFFER, currentFramebuffer);

      return filtered;
   },
});

const X3DLightingContext_default_ = X3DLightingContext;
;

/* harmony default export */ const Lighting_X3DLightingContext = (Namespace/* default */.A .add ("X3DLightingContext", X3DLightingContext_default_));
;// ./src/x_ite/Components/Navigation/OrthoViewpoint.js












function OrthoViewpoint (executionContext)
{
   Navigation_X3DViewpointNode .call (this, executionContext);

   this .addType (Base_X3DConstants .OrthoViewpoint);

   this .addChildObjects (Base_X3DConstants .inputOutput, "fieldOfViewOffset", new x_ite_Fields .SFVec4f (0, 0, 0, 0));

   // Legacy

   if (executionContext .getSpecificationVersion () <= 4.0)
      this .changeField (this ._fieldOfView .getAccessType (), "fieldOfView", new x_ite_Fields .MFFloat (... this ._fieldOfView));

   // Units

   this ._position         .setUnit ("length");
   this ._centerOfRotation .setUnit ("length");
   this ._fieldOfView      .setUnit ("length");

   // Private properties

   const
      browser      = this .getBrowser (),
      privateScene = browser .getPrivateScene ();

   this .projectionMatrix               = new Numbers_Matrix4 ();
   this .fieldOfViewOffsetInterpolator0 = new Interpolation_ScalarInterpolator (privateScene);
   this .fieldOfViewOffsetInterpolator1 = new Interpolation_ScalarInterpolator (privateScene);
   this .fieldOfViewOffsetInterpolator2 = new Interpolation_ScalarInterpolator (privateScene);
   this .fieldOfViewOffsetInterpolator3 = new Interpolation_ScalarInterpolator (privateScene);
}

Object .assign (Object .setPrototypeOf (OrthoViewpoint .prototype, Navigation_X3DViewpointNode .prototype),
{
   initialize ()
   {
      Navigation_X3DViewpointNode .prototype .initialize .call (this);

      this .fieldOfViewOffsetInterpolator0 ._key = new x_ite_Fields .MFFloat (0, 1);
      this .fieldOfViewOffsetInterpolator1 ._key = new x_ite_Fields .MFFloat (0, 1);
      this .fieldOfViewOffsetInterpolator2 ._key = new x_ite_Fields .MFFloat (0, 1);
      this .fieldOfViewOffsetInterpolator3 ._key = new x_ite_Fields .MFFloat (0, 1);

      this .fieldOfViewOffsetInterpolator0 .setup ();
      this .fieldOfViewOffsetInterpolator1 .setup ();
      this .fieldOfViewOffsetInterpolator2 .setup ();
      this .fieldOfViewOffsetInterpolator3 .setup ();

      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .fieldOfViewOffsetInterpolator0 ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .fieldOfViewOffsetInterpolator1 ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .fieldOfViewOffsetInterpolator2 ._set_fraction);
      this .easeInEaseOut ._modifiedFraction_changed .addFieldInterest (this .fieldOfViewOffsetInterpolator3 ._set_fraction);

      this .fieldOfViewOffsetInterpolator0 ._value_changed .addInterest ("set_fieldOfViewOffset__", this);
      this .fieldOfViewOffsetInterpolator1 ._value_changed .addInterest ("set_fieldOfViewOffset__", this);
      this .fieldOfViewOffsetInterpolator2 ._value_changed .addInterest ("set_fieldOfViewOffset__", this);
      this .fieldOfViewOffsetInterpolator3 ._value_changed .addInterest ("set_fieldOfViewOffset__", this);
   },
   set_fieldOfViewOffset__ ()
   {
      this ._fieldOfViewOffset [0] = this .fieldOfViewOffsetInterpolator0 ._value_changed .getValue ();
      this ._fieldOfViewOffset [1] = this .fieldOfViewOffsetInterpolator1 ._value_changed .getValue ();
      this ._fieldOfViewOffset [2] = this .fieldOfViewOffsetInterpolator2 ._value_changed .getValue ();
      this ._fieldOfViewOffset [3] = this .fieldOfViewOffsetInterpolator3 ._value_changed .getValue ();
   },
   resetUserOffsets ()
   {
      Navigation_X3DViewpointNode .prototype .resetUserOffsets .call (this);

      this ._fieldOfViewOffset [0] = 0;
      this ._fieldOfViewOffset [1] = 0;
      this ._fieldOfViewOffset [2] = 0;
      this ._fieldOfViewOffset [3] = 0;
   },
   getRelativeTransformation (fromViewpointNode)
   {
      const relative = Navigation_X3DViewpointNode .prototype .getRelativeTransformation .call (this, fromViewpointNode);

      if (fromViewpointNode .constructor === this .constructor)
      {
         relative .userMinimumX = fromViewpointNode .getUserMinimumX ();
         relative .userMinimumY = fromViewpointNode .getUserMinimumY ();
         relative .userMaximumX = fromViewpointNode .getUserMaximumX ();
         relative .userMaximumY = fromViewpointNode .getUserMaximumY ();
      }

      return relative;
   },
   setInterpolators (fromViewpointNode, relative)
   {
      if (fromViewpointNode .constructor === this .constructor)
      {
         const
            offset0 = relative .userMinimumX - this .getMinimumX (),
            offset1 = relative .userMinimumY - this .getMinimumY (),
            offset2 = relative .userMaximumX - this .getMaximumX (),
            offset3 = relative .userMaximumY - this .getMaximumY ();

         this .fieldOfViewOffsetInterpolator0 ._keyValue = new x_ite_Fields .MFFloat (offset0, this ._fieldOfViewOffset [0]);
         this .fieldOfViewOffsetInterpolator1 ._keyValue = new x_ite_Fields .MFFloat (offset1, this ._fieldOfViewOffset [1]);
         this .fieldOfViewOffsetInterpolator2 ._keyValue = new x_ite_Fields .MFFloat (offset2, this ._fieldOfViewOffset [2]);
         this .fieldOfViewOffsetInterpolator3 ._keyValue = new x_ite_Fields .MFFloat (offset3, this ._fieldOfViewOffset [3]);
         this .fieldOfViewScaleInterpolator   ._keyValue = new x_ite_Fields .MFFloat (1, this ._fieldOfViewScale .getValue ());

         this ._fieldOfViewOffset [0] = relative .offset0;
         this ._fieldOfViewOffset [1] = relative .offset1;
         this ._fieldOfViewOffset [2] = relative .offset2;
         this ._fieldOfViewOffset [3] = relative .offset3;
         this ._fieldOfViewScale      = 1;
      }
      else
      {
         this .fieldOfViewOffsetInterpolator0 ._keyValue = new x_ite_Fields .MFFloat (this ._fieldOfViewOffset [0], this ._fieldOfViewOffset [0]);
         this .fieldOfViewOffsetInterpolator1 ._keyValue = new x_ite_Fields .MFFloat (this ._fieldOfViewOffset [1], this ._fieldOfViewOffset [1]);
         this .fieldOfViewOffsetInterpolator2 ._keyValue = new x_ite_Fields .MFFloat (this ._fieldOfViewOffset [2], this ._fieldOfViewOffset [2]);
         this .fieldOfViewOffsetInterpolator3 ._keyValue = new x_ite_Fields .MFFloat (this ._fieldOfViewOffset [3], this ._fieldOfViewOffset [3]);
         this .fieldOfViewScaleInterpolator   ._keyValue = new x_ite_Fields .MFFloat (1, this ._fieldOfViewOffset .getValue ());

         this ._fieldOfViewOffset = this ._fieldOfViewOffset .getValue ();
         this ._fieldOfViewScale  = 1;
      }
   },
   getLogarithmicDepthBuffer ()
   {
      return false;
   },
   getFieldOfView ()
   {
      return new Numbers_Vector4 (
         this .getMinimumX (),
         this .getMinimumY (),
         this .getMaximumX (),
         this .getMaximumY (),
      );
   },
   setFieldOfView (value)
   {
      this ._fieldOfView = value;
   },
   getUserFieldOfView ()
   {
      return new Numbers_Vector4 (
         this .getUserMinimumX (),
         this .getUserMinimumY (),
         this .getUserMaximumX (),
         this .getUserMaximumY (),
      );
   },
   getMinimumX ()
   {
      return this ._fieldOfView [0] ?? -1;
   },
   getUserMinimumX ()
   {
      return (this .getMinimumX () + this ._fieldOfViewOffset [0]) * this ._fieldOfViewScale .getValue ();
   },
   getMinimumY ()
   {
      return this ._fieldOfView [1] ?? -1;
   },
   getUserMinimumY ()
   {
      return (this .getMinimumY () + this ._fieldOfViewOffset [1]) * this ._fieldOfViewScale .getValue ();
   },
   getMaximumX ()
   {
      return this ._fieldOfView [2] ?? 1;
   },
   getUserMaximumX ()
   {
      return (this .getMaximumX () + this ._fieldOfViewOffset [2]) * this ._fieldOfViewScale .getValue ();
   },
   getMaximumY ()
   {
      return this ._fieldOfView [3] ?? 1;
   },
   getUserMaximumY ()
   {
      return (this .getMaximumY () + this ._fieldOfViewOffset [3]) * this ._fieldOfViewScale .getValue ();
   },
   getSizeX ()
   {
      return this .getMaximumX () - this .getMinimumX ();
   },
   getUserSizeX ()
   {
      return this .getUserMaximumX () - this .getUserMinimumX ();
   },
   getSizeY ()
   {
      return this .getMaximumY () - this .getMinimumY ();
   },
   getUserSizeY ()
   {
      return this .getUserMaximumY () - this .getUserMinimumY ();
   },
   getMaxFarValue ()
   {
      return 1e4;
   },
   getScreenScale (point, viewport, screenScale)
   {
      const
         width  = viewport [2],
         height = viewport [3],
         sizeX  = this .getUserSizeX (),
         sizeY  = this .getUserSizeY (),
         aspect = width / height;

      if (aspect > sizeX / sizeY)
      {
         const s = sizeY / height;

         return screenScale .set (s, s, s);
      }
      else
      {
         const s = sizeX / width;

         return screenScale .set (s, s, s);
      }
   },
   getViewportSize: (() =>
   {
      const viewportSize = new Numbers_Vector2 ();

      return function (viewport, nearValue)
      {
         const
            width  = viewport [2],
            height = viewport [3],
            sizeX  = this .getUserSizeX (),
            sizeY  = this .getUserSizeY (),
            aspect = width / height;

         if (aspect > sizeX / sizeY)
            return viewportSize .set (sizeY * aspect, sizeY);

         return viewportSize .set (sizeX, sizeX / aspect);
      };
   })(),
   getLookAtDistance (bbox)
   {
      return bbox .size .norm () / 2 + 10;
   },
   getProjectionMatrixWithLimits (nearValue, farValue, viewport)
   {
      const
         width  = viewport [2],
         height = viewport [3],
         aspect = width / height,
         sizeX  = this .getUserSizeX (),
         sizeY  = this .getUserSizeY ();

      if (aspect > sizeX / sizeY)
      {
         const
            center  = (this .getUserMinimumX () + this .getUserMaximumX ()) / 2,
            size1_2 = (sizeY * aspect) / 2;

         return Geometry_Camera .ortho (center - size1_2, center + size1_2, this .getUserMinimumY (), this .getUserMaximumY (), nearValue, farValue, this .projectionMatrix);
      }
      else
      {
         const
            center  = (this .getUserMinimumY () + this .getUserMaximumY ()) / 2,
            size1_2 = (sizeX / aspect) / 2;

         return Geometry_Camera .ortho (this .getUserMinimumX (), this .getUserMaximumX (), center - size1_2, center + size1_2, nearValue, farValue, this .projectionMatrix);
      }
   },
   viewAll (bbox)
   {
      Navigation_X3DViewpointNode .prototype .viewAll .call (this, bbox);

      const
         size   = bbox .size,
         scaleX = size .x / this .getSizeX (),
         scaleY = size .y / this .getSizeY (),
         scale  = Math .max (scaleX, scaleY) * 1.1;

      this ._fieldOfViewOffset [0] = this .getMinimumX () * scale - this .getMinimumX ();
      this ._fieldOfViewOffset [1] = this .getMinimumY () * scale - this .getMinimumY ();
      this ._fieldOfViewOffset [2] = this .getMaximumX () * scale - this .getMaximumX ();
      this ._fieldOfViewOffset [3] = this .getMaximumY () * scale - this .getMaximumY ();
   },
   convertFields (fields)
   {
      // Don't check for specification version, it will not work with Sunrize.

      fields .find (field => field .getName () === "fieldOfView")
         ?.assign (new x_ite_Fields .SFVec4f (... this ._fieldOfView));

      return fields;
   },
});

Object .defineProperties (OrthoViewpoint,
{
   ... Core_X3DNode .getStaticProperties ("OrthoViewpoint", "Navigation", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "position",          new x_ite_Fields .SFVec3f (0, 0, 10)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "orientation",       new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "centerOfRotation",  new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fieldOfView",       new x_ite_Fields .SFVec4f (-1, -1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "nearDistance",      new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "farDistance",       new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "viewAll",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "jump",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "retainUserOffsets", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",          new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "navigationInfo",    new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const OrthoViewpoint_default_ = OrthoViewpoint;
;

/* harmony default export */ const Navigation_OrthoViewpoint = (Namespace/* default */.A .add ("OrthoViewpoint", OrthoViewpoint_default_));
;// ./src/x_ite/Browser/Navigation/X3DViewer.js










function X3DViewer (executionContext, navigationInfoNode)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .addChildObjects (Base_X3DConstants .outputOnly, "isActive", new x_ite_Fields .SFBool ());

   // Private properties

   this .navigationInfoNode = navigationInfoNode;
}

Object .assign (Object .setPrototypeOf (X3DViewer .prototype, Base_X3DBaseNode .prototype),
{
   isCollidable ()
   {
      return false;
   },
   isActive ()
   {
      return this ._isActive .getValue ();
   },
   getActiveLayer ()
   {
      return this .getBrowser () .getActiveLayer ();
   },
   getViewport ()
   {
      return this .getBrowser () .getActiveLayer () .getViewport () .getRectangle ();
   },
   getNavigationInfo ()
   {
      return this .navigationInfoNode;
   },
   getActiveViewpoint ()
   {
      return this .getBrowser () .getActiveLayer () .getViewpoint ();
   },
   getStraightenHorizon ()
   {
      const browser = this .getBrowser ();

      return browser .getBrowserOption ("StraightenHorizon") || !! browser .getPose ();
   },
   getButton (button)
   {
      // If Alt key is pressed and button 0, then emulate button 1 (middle).
      if (button === 0)
      {
         if (this .getBrowser () .getAltKey ())
         {
            return 1;
         }
      }

      return button;
   },
   isPointerInRectangle: (() =>
   {
      const pointer = new Numbers_Vector2 ();

      return function (x, y)
      {
         const
            browser   = this .getBrowser (),
            rectangle = this .getViewport ();

         return browser .isPointerInRectangle (rectangle, pointer .set (x, y));
      };
   })(),
   getPointOnCenterPlane: (() =>
   {
      const
         distance = new Numbers_Vector3 (),
         far      = new Numbers_Vector3 ();

      return function (x, y, result)
      {
         const
            navigationInfoNode = this .getNavigationInfo (),
            viewpointNode      = this .getActiveViewpoint (),
            viewport           = this .getViewport (),
            nearValue          = viewpointNode .getNearDistance (navigationInfoNode),
            farValue           = viewpointNode .getFarDistance (navigationInfoNode),
            projectionMatrix   = viewpointNode .getProjectionMatrixWithLimits (nearValue, farValue, viewport);

         // Far plane point
         Geometry_ViewVolume .unProjectPoint (x, y, 0.9, Numbers_Matrix4 .IDENTITY, projectionMatrix, viewport, far);

         if (viewpointNode instanceof Navigation_OrthoViewpoint)
            return result .set (far .x, far .y, -this .getDistanceToCenter (distance) .norm ());

         const direction = far .normalize ();

         return result .assign (direction) .multiply (this .getDistanceToCenter (distance) .norm () / direction .dot (Numbers_Vector3 .NEGATIVE_Z_AXIS));
      };
   })(),
   getDistanceToCenter (distance, positionOffset)
   {
      const viewpointNode = this .getActiveViewpoint ();

      return (distance
         .assign (viewpointNode .getPosition ())
         .add (positionOffset || viewpointNode ._positionOffset .getValue ())
         .subtract (viewpointNode .getUserCenterOfRotation ()));
   },
   trackballProjectToSphere (x, y, vector)
   {
      const viewport = this .getViewport ();

      x = (x - viewport [0]) / viewport [2] - 0.5;
      y = (y - viewport [1]) / viewport [3] - 0.5;

      return vector .set (x, y, tbProjectToSphere (0.5, x, y));
   },
   lookAtPoint (x, y, straightenHorizon)
   {
      if (!this .touch (x, y))
         return;

      const
         viewpointNode = this .getActiveViewpoint (),
         hit           = this .getBrowser () .getHit ();

      viewpointNode .lookAtPoint (this .getActiveLayer (), hit .point, 1, 2 - 1.618034, straightenHorizon);
   },
   lookAtBBox (x, y, straightenHorizon)
   {
      if (!this .touch (x, y))
         return;

      const
         viewpointNode = this .getActiveViewpoint (),
         hit           = this .getBrowser () .getHit ();

      const bbox = hit .shapeNode .getBBox (new Geometry_Box3 ())
         .multRight (hit .modelViewMatrix)
         .multRight (viewpointNode .getCameraSpaceMatrix ());

      viewpointNode .lookAtBBox (this .getActiveLayer (), bbox, 1, 2 - 1.618034, straightenHorizon);
   },
   touch (x, y)
   {
      return this .getBrowser () .touch (x, y);
   },
   disconnect ()
   { },
});

function tbProjectToSphere (r, x, y)
{
   const d = Math .hypot (x, y);

   if (d < r * Math .SQRT1_2) // Inside sphere
   {
      return Math .sqrt (r * r - d * d);
   }

   // On hyperbola

   const t = r / Math .SQRT2;

   return t * t / d;
}

const X3DViewer_default_ = X3DViewer;
;

/* harmony default export */ const Navigation_X3DViewer = (Namespace/* default */.A .add ("X3DViewer", X3DViewer_default_));
;// ./src/x_ite/Components/Followers/X3DFollowerNode.js




function X3DFollowerNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DFollowerNode);

   this .buffer = [ ];

   // Auxillary variables
   this .a      = this .getVector ();
   this .vector = this .getVector ();
}

Object .assign (Object .setPrototypeOf (X3DFollowerNode .prototype, Core_X3DChildNode .prototype),
{
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this .getLive () .addInterest ("set_live__", this);
   },
   getBuffer ()
   {
      return this .buffer;
   },
   getValue ()
   {
      return this ._set_value .getValue ();
   },
   getDestination ()
   {
      return this ._set_destination .getValue ();
   },
   getInitialValue ()
   {
      return this ._initialValue .getValue ();
   },
   getInitialDestination ()
   {
      return this ._initialDestination .getValue ();
   },
   setValue (value)
   {
      this ._value_changed = value;
   },
   setDestination (value)
   {
      this .destination .assign (value);
   },
   duplicate (value)
   {
      return value .copy ();
   },
   assign (buffer, i, value)
   {
      buffer [i] .assign (value);
   },
   equals (lhs, rhs, tolerance)
   {
      return this .a .assign (lhs) .subtract (rhs) .norm () < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return this .vector .assign (source) .lerp (destination, weight);
   },
   set_live__ ()
   {
      if (this .getLive () .getValue () && this ._isActive .getValue ())
      {
         this .getBrowser () .prepareEvents () .addInterest ("prepareEvents", this);
         this .getBrowser () .addBrowserEvent ();
      }
      else
         this .getBrowser () .prepareEvents () .removeInterest ("prepareEvents", this);
   },
   set_active__ (value)
   {
      if (value === this ._isActive .getValue ())
         return;

      this ._isActive = value;

      this .set_live__ ();
   },
});

Object .defineProperties (X3DFollowerNode, Core_X3DNode .getStaticProperties ("X3DFollowerNode", "Followers", 1));

const X3DFollowerNode_default_ = X3DFollowerNode;
;

/* harmony default export */ const Followers_X3DFollowerNode = (Namespace/* default */.A .add ("X3DFollowerNode", X3DFollowerNode_default_));
;// ./src/x_ite/Components/Followers/X3DChaserNode.js




function X3DChaserNode (executionContext)
{
   Followers_X3DFollowerNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DChaserNode);

   this .destination   = null;
   this .previousValue = null;
   this .bufferEndTime = 0;
   this .stepTime      = 0;

   // Auxillary variables
   this .deltaOut = this .getVector ();
}

Object .assign (Object .setPrototypeOf (X3DChaserNode .prototype, Followers_X3DFollowerNode .prototype),
{
   initialize ()
   {
      Followers_X3DFollowerNode .prototype .initialize .call (this);

      this ._set_value       .addInterest ("set_value__",       this);
      this ._set_destination .addInterest ("set_destination__", this);
      this ._duration        .addInterest ("set_duration__",    this);

      this .set_duration__ ();

      const
         buffer             = this .getBuffer (),
         initialValue       = this .getInitialValue (),
         initialDestination = this .getInitialDestination (),
         numBuffers         = this .getNumBuffers ();

      this .bufferEndTime = this .getBrowser () .getCurrentTime ();
      this .previousValue = this .duplicate (initialValue);

      buffer [0] = this .duplicate (initialDestination);

      for (let i = 1; i < numBuffers; ++ i)
         buffer [i] = this .duplicate (initialValue);

      this .destination = this .duplicate (initialDestination);

      if (this .equals (initialDestination, initialValue, this .getTolerance ()))
         this .setValue (initialDestination);

      else
         this .set_active__ (true);
   },
   getNumBuffers ()
   {
      return 60;
   },
   getTolerance ()
   {
      return 1e-8;
   },
   getArray ()
   {
      return this .getVector ();
   },
   setPreviousValue (value)
   {
      this .previousValue .assign (value);
   },
   step (value1, value2, t)
   {
      this .output .add (this .deltaOut .assign (value1) .subtract (value2) .multiply (t));
   },
   stepResponse (t)
   {
      if (t <= 0)
         return 0;

      const duration = this ._duration .getValue ();

      if (t >= duration)
         return 1;

      return 0.5 - 0.5 * Math .cos ((t / duration) * Math .PI);
   },
   set_value__ ()
   {
      if (!this ._isActive .getValue ())
         this .bufferEndTime = this .getBrowser () .getCurrentTime ();

      const
         buffer = this .getBuffer (),
         value  = this .getValue ();

      for (let i = 0, length = buffer .length; i < length; ++ i)
         this .assign (buffer, i, value);

      this .setPreviousValue (value);
      this .setValue (value);

      this .set_active__ (true);
   },
   set_destination__ ()
   {
      this .setDestination (this .getDestination ());

      if (!this ._isActive .getValue ())
         this .bufferEndTime = this .getBrowser () .getCurrentTime ();

      this .set_active__ (true);
   },
   set_duration__ ()
   {
      this .stepTime = this ._duration .getValue () / this .getNumBuffers ();
   },
   prepareEvents ()
   {
      const
         buffer     = this .getBuffer (),
         numBuffers = buffer .length,
         fraction   = this .updateBuffer ();

      this .output = this .interpolate (this .previousValue,
                                          buffer [numBuffers - 1],
                                          this .stepResponse ((numBuffers - 1 + fraction) * this .stepTime));

      for (let i = numBuffers - 2; i >= 0; -- i)
      {
         this .step (buffer [i], buffer [i + 1], this .stepResponse ((i + fraction) * this .stepTime));
      }

      this .setValue (this .output);

      if (this .equals (this .output, this .destination, this .getTolerance ()))
         this .set_active__ (false);
   },
   updateBuffer ()
   {
      const
         buffer     = this .getBuffer (),
         numBuffers = buffer .length;

      let fraction = (this .getBrowser () .getCurrentTime () - this .bufferEndTime) / this .stepTime;

      if (fraction >= 1)
      {
         const seconds = Math .floor (fraction);

         fraction -= seconds;

         if (seconds < numBuffers)
         {
            this .setPreviousValue (buffer [numBuffers - seconds]);

            for (let i = numBuffers - 1; i >= seconds; -- i)
            {
               this .assign (buffer, i, buffer [i - seconds])
            }

            for (let i = 0; i < seconds; ++ i)
            {
               const alpha = i / seconds;

               this .assign (buffer, i, this .interpolate (this .destination, buffer [seconds], alpha))
            }
         }
         else
         {
            this .setPreviousValue (seconds == numBuffers ? buffer [0] : this .destination);

            for (let i = 0; i < numBuffers; ++ i)
               this .assign (buffer, i, this .destination);
         }

         this .bufferEndTime += seconds * this .stepTime;
      }

      return fraction;
   },
});

Object .defineProperties (X3DChaserNode, Core_X3DNode .getStaticProperties ("X3DChaserNode", "Followers", 1));

const X3DChaserNode_default_ = X3DChaserNode;
;

/* harmony default export */ const Followers_X3DChaserNode = (Namespace/* default */.A .add ("X3DChaserNode", X3DChaserNode_default_));
;// ./src/x_ite/Components/Followers/PositionChaser.js








function PositionChaser (executionContext)
{
   Followers_X3DChaserNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionChaser);
}

Object .assign (Object .setPrototypeOf (PositionChaser .prototype, Followers_X3DChaserNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
});

Object .defineProperties (PositionChaser,
{
   ... Core_X3DNode .getStaticProperties ("PositionChaser", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFVec3f ()),
      ]),
      enumerable: true,
   },
});

const PositionChaser_default_ = PositionChaser;
;

/* harmony default export */ const Followers_PositionChaser = (Namespace/* default */.A .add ("PositionChaser", PositionChaser_default_));
;// ./src/x_ite/Components/Followers/OrientationChaser.js








const
   OrientationChaser_a        = new Numbers_Rotation4 (),
   OrientationChaser_rotation = new Numbers_Rotation4 ();

function OrientationChaser (executionContext)
{
   Followers_X3DChaserNode .call (this, executionContext);

   this .addType (Base_X3DConstants .OrientationChaser);

   // Units

   this ._set_value          .setUnit ("angle");
   this ._set_destination    .setUnit ("angle");
   this ._initialValue       .setUnit ("angle");
   this ._initialDestination .setUnit ("angle");
   this ._value_changed      .setUnit ("angle");
}

Object .assign (Object .setPrototypeOf (OrientationChaser .prototype, Followers_X3DChaserNode .prototype),
{
   getVector ()
   {
      return new Numbers_Rotation4 ();
   },
   equals (lhs, rhs, tolerance)
   {
      OrientationChaser_a .assign (lhs) .inverse () .multRight (rhs);

      return Math .abs (OrientationChaser_a .angle) < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return OrientationChaser_rotation .assign (source) .slerp (destination, weight);
   },
   step (value1, value2, t)
   {
      this .deltaOut .assign (value2) .inverse () .multRight (value1) .multLeft (this .output);

      this .output .slerp (this .deltaOut, t);
   },
});

Object .defineProperties (OrientationChaser,
{
   ... Core_X3DNode .getStaticProperties ("OrientationChaser", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFRotation (0, 1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFRotation (0, 1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFRotation ()),
      ]),
      enumerable: true,
   },
});

const OrientationChaser_default_ = OrientationChaser;
;

/* harmony default export */ const Followers_OrientationChaser = (Namespace/* default */.A .add ("OrientationChaser", OrientationChaser_default_));
;// ./src/x_ite/Browser/Navigation/ExamineViewer.js
/* provided dependency */ var jquery_mousewheel = __webpack_require__(395);
/* provided dependency */ var ExamineViewer_$ = __webpack_require__(44).A;









void (typeof jquery_mousewheel); // import plugin

const macOS = /Mac OS X/i .test (navigator .userAgent);

const
   SPIN_RELEASE_TIME = 20,
   SPIN_ANGLE        = Math_Algorithm .radians (2),
   SPIN_FACTOR       = 0.3,
   SCROLL_FACTOR     = macOS ? 1 / 120 : 1 / 20,
   MOVE_TIME         = 0.2,
   ROTATE_TIME       = 0.2,
   DISK_ANGLE        = Math_Algorithm .radians (15),
   CRITICAL_ANGLE    = Math_Algorithm .radians (0.1);

function ExamineViewer (executionContext, navigationInfo)
{
   Navigation_X3DViewer .call (this, executionContext, navigationInfo);

   this .button                   = -1;
   this .orientationOffset        = new Numbers_Rotation4 ();
   this .fromVector               = new Numbers_Vector3 ();
   this .toVector                 = new Numbers_Vector3 ();
   this .fromPointer              = new Numbers_Vector2 ();
   this .fromPoint                = new Numbers_Vector3 ();
   this .toPoint                  = new Numbers_Vector3 ();
   this .startOrientation         = new Numbers_Rotation4 ();
   this .roll                     = new Numbers_Rotation4 ();
   this .rotation                 = new Numbers_Rotation4 ();
   this .deltaRotation            = new Numbers_Rotation4 ();
   this .direction                = new Numbers_Vector3 ();
   this .axis                     = new Numbers_Vector3 ();

   this .touchMode                = 0;
   this .touch1                   = new Numbers_Vector2 ();
   this .touch2                   = new Numbers_Vector2 ();
   this .tapStart                 = 0;
   this .dblTapInterval           = 0.4;

   this .initialPositionOffset    = new Numbers_Vector3 ();
   this .initialOrientationOffset = new Numbers_Rotation4 ();
   this .positionChaser           = new Followers_PositionChaser (executionContext);
   this .centerOfRotationChaser   = new Followers_PositionChaser (executionContext);
   this .rotationChaser           = new Followers_OrientationChaser (executionContext);

   this .timeSensor = new Time_TimeSensor (executionContext);
}

Object .assign (Object .setPrototypeOf (ExamineViewer .prototype, Navigation_X3DViewer .prototype),
{
   initialize ()
   {
      Navigation_X3DViewer .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      // Disconnect from spin.

      this .getNavigationInfo () ._transitionStart .addInterest ("disconnect", this);
      browser .getBrowserOptions () ._StraightenHorizon .addInterest ("disconnect", this);
      browser ._activeViewpoint .addInterest ("set_activeViewpoint__", this);

      // Bind pointing device events.

      element .on ("mousedown.ExamineViewer",  this .mousedown  .bind (this));
      element .on ("mouseup.ExamineViewer",    this .mouseup    .bind (this));
      element .on ("dblclick.ExamineViewer",   this .dblclick   .bind (this));
      element .on ("mousewheel.ExamineViewer", this .mousewheel .bind (this));

      element .on ("touchstart.ExamineViewer",  this .touchstart .bind (this));
      element .on ("touchend.ExamineViewer",    this .touchend   .bind (this));

      // Setup scroll chaser.

      this .positionChaser ._duration = MOVE_TIME;
      this .positionChaser .setup ();

      this .centerOfRotationChaser ._duration = MOVE_TIME;
      this .centerOfRotationChaser .setup ();

      this .rotationChaser ._duration = ROTATE_TIME;
      this .rotationChaser .setup ();

      this .timeSensor ._loop     = true;
      this .timeSensor ._stopTime = Date .now () / 1000;
      this .timeSensor .setup ();

      this .timeSensor ._fraction_changed  .addInterest ("spin", this);

      this .set_activeViewpoint__ ();
   },
   set_activeViewpoint__ ()
   {
      if (this .getStraightenHorizon ())
      {
         const viewpoint = this .getActiveViewpoint ();

         if (viewpoint)
         {
            this .startOrientation .assign (viewpoint .getUserOrientation ());

            viewpoint ._orientationOffset = this .getOrientationOffset (Numbers_Rotation4 .IDENTITY, Numbers_Rotation4 .IDENTITY, viewpoint ._orientationOffset .getValue ());
         }
      }

      this .disconnect ();
   },
   mousedown (event)
   {
      if (this .button >= 0)
         return;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      switch (this .getButton (event .button))
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Start rotate.

            this .button = event .button;

            ExamineViewer_$(document) .on ("mouseup.ExamineViewer"   + this .getId (), this .mouseup   .bind (this));
            ExamineViewer_$(document) .on ("mousemove.ExamineViewer" + this .getId (), this .mousemove .bind (this));
            ExamineViewer_$(document) .on ("touchend.ExamineViewer"  + this .getId (), this .touchend  .bind (this));
            ExamineViewer_$(document) .on ("touchmove.ExamineViewer" + this .getId (), this .touchmove .bind (this));

            this .disconnect ();
            this .getActiveViewpoint () .transitionStop ();
            this .getBrowser () .setCursor ("MOVE");
            this .startRotate (x, y, 0);

            this .motionTime = Date .now ();

            this ._isActive = true;
            break;
         }
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Start pan.

            this .button = event .button;

            ExamineViewer_$(document) .on ("mouseup.ExamineViewer"   + this .getId (), this .mouseup   .bind (this));
            ExamineViewer_$(document) .on ("mousemove.ExamineViewer" + this .getId (), this .mousemove .bind (this));
            ExamineViewer_$(document) .on ("touchend.ExamineViewer"  + this .getId (), this .touchend  .bind (this));
            ExamineViewer_$(document) .on ("touchmove.ExamineViewer" + this .getId (), this .touchmove .bind (this));

            this .disconnect ();
            this .getActiveViewpoint () .transitionStop ();
            this .getBrowser () .setCursor ("MOVE");
            this .startPan (x, y);

            this ._isActive = true;
            break;
         }
      }
   },
   mouseup (event)
   {
      if (event .button !== this .button)
         return;

      this .button = -1;

      ExamineViewer_$(document) .off (".ExamineViewer" + this .getId ());

      switch (this .getButton (event .button))
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // End rotate.

            this .getBrowser () .setCursor ("DEFAULT");

            if (Math .abs (this .deltaRotation .angle) > SPIN_ANGLE && Date .now () - this .motionTime < SPIN_RELEASE_TIME)
               this .addSpinning (this .deltaRotation);

            this ._isActive = false;
            break;
         }
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // End pan.

            this .getBrowser () .setCursor ("DEFAULT");

            this ._isActive = false;
            break;
         }
      }
   },
   dblclick (event)
   {
      // Stop event propagation.

      event .preventDefault ();

      // Look at.

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      this .disconnect ();

      if (this .getBrowser () .getAltKey ())
         this .lookAtPoint (x, y, this .getStraightenHorizon ());
      else
         this .lookAtBBox (x, y, this .getStraightenHorizon ());
   },
   mousemove (event)
   {
      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      switch (this .getButton (this .button))
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Rotate view around Viewpoint.centerOfRotation.

            this .rotate (x, y);

            this .motionTime = Date .now ();
            break;
         }
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Move view along center plane.

            this .pan (x, y);
            break;
         }
      }
   },
   mousewheel (event)
   {
      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      // Stop event propagation.

      event .preventDefault ();

      // Change viewpoint position.

      const viewpoint = this .getActiveViewpoint ();

      this .removeSpinning ();
      viewpoint .transitionStop ();

      this .zoom (event .zoomFactor || SCROLL_FACTOR, event .deltaY);
   },
   touchstart (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // Start rotate (button 0).

            event .button = 0;
            event .pageX  = touches [0] .pageX;
            event .pageY  = touches [0] .pageY;

            this .mousedown (event);

            // Remember tap.

            this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
            break;
         }
         case 2:
         {
            // End rotate (button 0).

            this .touchend (event);

            // Start move (button 1).

            event .button = 1;
            event .pageX  = (touches [0] .pageX + touches [1] .pageX) / 2;
            event .pageY  = (touches [0] .pageY + touches [1] .pageY) / 2;

            this .mousedown (event);

            // Start zoom (mouse wheel).

            this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
            this .touch2 .set (touches [1] .pageX, touches [1] .pageY);
            break;
         }
         case 3:
         {
            // End move (button 1).
            this .touchend (event);
            break;
         }
      }
   },
   touchend (event)
   {
      switch (this .button)
      {
         case 0:
         {
            // End rotate (button 0).

            event .button = 0;
            event .pageX  = this .touch1 .x;
            event .pageY  = this .touch1 .y;

            this .mouseup (event);

            // Start dblclick (button 0).

            if (this .tapedTwice)
            {
               this .dblclick (event);
            }
            else
            {
               this .tapedTwice = true;

               setTimeout (() => this .tapedTwice = false, 300);
            }

            break;
         }
         case 1:
         {
            // End move (button 1).

            this .touchMode = 0;
            event .button   = 1;

            this .mouseup (event);
            break;
         }
      }
   },
   touchmove: (() =>
   {
      const
         MOVE_ANGLE   = 0.7,
         ZOOM_ANGLE   = -0.7,
         touch1Change = new Numbers_Vector2 (),
         touch2Change = new Numbers_Vector2 ();

      return function (event)
      {
         const touches = event .originalEvent .touches;

         switch (touches .length)
         {
            case 1:
            {
               // Rotate (button 0).

               event .pageX = touches [0] .pageX;
               event .pageY = touches [0] .pageY;

               this .mousemove (event);
               break;
            }
            case 2:
            {
               touch1Change .set (touches [0] .pageX, touches [0] .pageY) .subtract (this .touch1) .normalize ();
               touch2Change .set (touches [1] .pageX, touches [1] .pageY) .subtract (this .touch2) .normalize ();

               const
                  move = touch1Change .dot (touch2Change) > MOVE_ANGLE,
                  zoom = touch1Change .dot (touch2Change) < ZOOM_ANGLE,
                  mode = this .touchMode || (move ? 1 : (zoom ? 2 : 0));

               switch (mode)
               {
                  case 1:
                  {
                     // Move (button 1).

                     this .touchMode = 1;

                     event .pageX = (touches [0] .pageX + touches [1] .pageX) / 2;
                     event .pageY = (touches [0] .pageY + touches [1] .pageY) / 2;

                     this .mousemove (event);

                     break;
                  }
                  case 2:
                  {
                     // Zoom (mouse wheel).

                     this .touchMode = 2;

                     const distance1 = this .touch1 .distance (this .touch2);

                     this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
                     this .touch2 .set (touches [1] .pageX, touches [1] .pageY);

                     const
                        distance2 = this .touch1 .distance (this .touch2),
                        delta     = distance2 - distance1;

                     event .deltaY     = delta;
                     event .zoomFactor = Math .abs (delta) / ExamineViewer_$(window) .width ();

                     event .pageX = (touches [0] .pageX + touches [1] .pageX) / 2;
                     event .pageY = (touches [0] .pageY + touches [1] .pageY) / 2;

                     this .mousewheel (event);

                     break;
                  }
               }

               this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
               this .touch2 .set (touches [1] .pageX, touches [1] .pageY);
               break;
            }
         }
      };
   })(),
   startRotate (x, y, negate)
   {
      if (this .getStraightenHorizon ())
      {
         const viewpoint = this .getActiveViewpoint ();

         this .fromPointer .set (x, y);
         this .startOrientation .assign (viewpoint .getUserOrientation ());
         this .lastRoll = 0;
         this .negate   = negate;
      }
      else
      {
         this .trackballProjectToSphere (x, y, this .fromVector);
      }
   },
   rotate: (() =>
   {
      const
         translation = new Numbers_Vector2 (),
         axis        = new Numbers_Vector3 ();

      return function (x, y)
      {
         if (this .getStraightenHorizon ())
         {
            const
               viewpoint = this .getActiveViewpoint (),
               upVector  = viewpoint .getUpVector (true);

            translation .set (x, y) .subtract (this .fromPointer);

            const
               pixelPerRevolutionX = this .getViewport () [2] * 2,
               pixelPerRevolutionY = this .getViewport () [3] * 2,
               startRoll           = Math .acos (Math_Algorithm .clamp (this .startOrientation .multVecRot (axis .assign (Numbers_Vector3 .Z_AXIS)) .dot (upVector), -1, 1)),
               roll                = Math .PI * 2 * +translation .y / pixelPerRevolutionY,
               clampedRoll         = Math_Algorithm .clamp (startRoll + roll, CRITICAL_ANGLE, Math .PI - CRITICAL_ANGLE) - startRoll;

            // Adjust fromPointer y.

            if (this .lastRoll - Math .abs (roll) > 0)
               this .fromPointer .y += (roll - clampedRoll) / (Math .PI * 2) * pixelPerRevolutionY;

            this .lastRoll = Math .abs (roll);

            // Check disk angle.

            if (!this .negate)
            {
               if (Math .PI / 2 - Math .abs (startRoll - Math .PI / 2) < DISK_ANGLE)
               {
                  if (y - this .getViewport () [1] > this .getViewport () [3] / 2)
                     this .negate = startRoll < Math .PI / 4 ? -1 : 1;
                  else
                     this .negate = startRoll > Math .PI / 4 ? -1 : 1;
               }
               else
               {
                  this .negate = 1;
               }
            }

            // Determine roll and rotation.

            this .deltaRotation .assign (this .rotation);
            this .roll .set (1, 0, 0, clampedRoll);
            this .rotation .setAxisAngle (upVector, Math .PI * 2 * -translation .x / pixelPerRevolutionX * this .negate);
            this .deltaRotation .inverse () .multRight (this .rotation);
         }
         else
         {
            const toVector = this .trackballProjectToSphere (x, y, this .toVector);

            // Determine roll and rotation.

            this .deltaRotation .assign (this .rotation);
            this .roll .assign (Numbers_Rotation4 .IDENTITY);
            this .rotation .setVectors (toVector, this .fromVector);
            this .deltaRotation .inverse () .multRight (this .rotation);
         }

         this .addRotate (this .roll, this .rotation, this .deltaRotation);
      };
   })(),
   addRotate (roll, rotation, deltaRotation)
   {
      const viewpoint = this .getActiveViewpoint ();

      if (this .rotationChaser ._value_changed .hasInterest ("set_rotation__", this))
      {
         // console .warn ("active")

         this .getOrientationOffset (roll, rotation, this .initialOrientationOffset);

         this .rotationChaser ._set_destination = rotation;
      }
      else
      {
         // console .warn ("start")

         this .rotationChaser ._value_changed .addInterest ("set_rotation__", this);

         this .initialOrientationOffset .assign (viewpoint ._orientationOffset .getValue ());
         this .initialPositionOffset    .assign (viewpoint ._positionOffset    .getValue ());

         this .getOrientationOffset (roll, rotation, this .initialOrientationOffset);

         this .rotationChaser ._set_value       = Numbers_Rotation4 .IDENTITY;
         this .rotationChaser ._set_destination = rotation;
      }
   },
   addSpinning (deltaRotation)
   {
      this .disconnect ();

      if (this .getStraightenHorizon ())
      {
         const
            viewpoint            = this .getActiveViewpoint (),
            upVector             = viewpoint .getUpVector (true),
            userPosition         = viewpoint .getUserPosition (),
            userCenterOfRotation = viewpoint .getUserCenterOfRotation (),
            direction            = userPosition .copy () .subtract (userCenterOfRotation),
            angle                = Math .min (Math .abs (deltaRotation .angle), Math .PI / 4);

         this .axis .assign (upVector);

         if (Math .sign (deltaRotation .getAxis () .dot (upVector)) !== Math .sign (deltaRotation .angle))
            this .axis .negate ();

         this .timeSensor ._cycleInterval = Math .PI / (angle * SPIN_FACTOR * 25);
         this .timeSensor ._startTime     = Date .now () / 1000;

         const lookAtRotation = viewpoint .getLookAtRotation (userPosition, userCenterOfRotation);

         this .direction .assign (direction);
         this .orientationOffset .assign (viewpoint .getUserOrientation ()) .multRight (lookAtRotation .inverse ());
      }
      else
      {
         this .getBrowser () .prepareEvents () .addInterest ("spin", this);
         this .rotation .assign (deltaRotation);
      }
   },
   removeSpinning ()
   {
      const browser = this .getBrowser ();

      this .timeSensor ._stopTime = Date .now () / 1000;
      browser .prepareEvents () .removeInterest ("spin", this);
   },
   spin: (() =>
   {
      const
         direction         = new Numbers_Vector3 (),
         positionOffset    = new Numbers_Vector3 (),
         orientationOffset = new Numbers_Rotation4 (),
         rotation          = new Numbers_Rotation4 ();

      return function ()
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .getStraightenHorizon ())
         {
            const
               userCenterOfRotation = viewpoint .getUserCenterOfRotation (),
               fraction             = this .timeSensor ._fraction_changed .getValue (),
               rotation             = new Numbers_Rotation4 (... this .axis, 2 * Math .PI * fraction),
               userPosition         = rotation .multVecRot (direction .assign (this .direction)) .add (userCenterOfRotation),
               lookAtRotation       = viewpoint .getLookAtRotation (userPosition, viewpoint .getUserCenterOfRotation ());

            positionOffset .assign (userPosition) .subtract (viewpoint .getPosition ());

            orientationOffset .assign (viewpoint .getOrientation ()) .inverse ()
               .multRight (this .orientationOffset) .multRight (lookAtRotation);

            viewpoint ._positionOffset    = positionOffset;
            viewpoint ._orientationOffset = orientationOffset;
         }
         else
         {
            rotation .assign (Numbers_Rotation4 .IDENTITY) .slerp (this .rotation, SPIN_FACTOR * 60 / this .getBrowser () .getCurrentFrameRate ());

            this .orientationOffset .assign (viewpoint ._orientationOffset .getValue ());

            viewpoint ._orientationOffset = this .getOrientationOffset (Numbers_Rotation4 .IDENTITY, rotation, this .orientationOffset);
            viewpoint ._positionOffset    = this .getPositionOffset (viewpoint ._positionOffset .getValue (), this .orientationOffset, viewpoint ._orientationOffset .getValue ());
         }
      };
   })(),
   startPan (x, y)
   {
      this .getPointOnCenterPlane (x, y, this .fromPoint);
   },
   pan: (() =>
   {
      const fromPoint = new Numbers_Vector3 ();

      return function  (x, y)
      {
         const
            viewpoint   = this .getActiveViewpoint (),
            toPoint     = this .getPointOnCenterPlane (x, y, this .toPoint),
            translation = viewpoint .getUserOrientation () .multVecRot (fromPoint .assign (this .fromPoint) .subtract (toPoint));

         this .addMove (translation, translation);

         this .fromPoint .assign (toPoint);
      };
   })(),
   zoom: (() =>
   {
      const
         step        = new Numbers_Vector3 (),
         translation = new Numbers_Vector3 ();

      return function (zoomFactor, deltaY)
      {
         // Change viewpoint position.

         const viewpoint = this .getActiveViewpoint ();

         this .getDistanceToCenter (step) .multiply (zoomFactor);
         viewpoint .getUserOrientation () .multVecRot (translation .set (0, 0, step .norm ()));

         if (deltaY > 0)
            this .addMove (translation .negate (), Numbers_Vector3 .ZERO);

         else if (deltaY < 0)
            this .addMove (translation, Numbers_Vector3 .ZERO);
      };
   })(),
   addMove: (() =>
   {
      const
         positionOffset         = new Numbers_Vector3 (),
         centerOfRotationOffset = new Numbers_Vector3 ();

      return function (positionOffsetChange, centerOfRotationOffsetChange)
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .positionChaser ._value_changed .hasInterest ("set_positionOffset__", this))
         {
            positionOffset
               .assign (this .positionChaser ._set_destination .getValue ())
               .add (positionOffsetChange);

            this .positionChaser ._set_destination = positionOffset;
         }
         else
         {
            this .positionChaser ._value_changed .addInterest ("set_positionOffset__", this);

            positionOffset
               .assign (viewpoint ._positionOffset .getValue ())
               .add (positionOffsetChange);

            this .positionChaser ._set_value       = viewpoint ._positionOffset;
            this .positionChaser ._set_destination = positionOffset;
         }

         if (this .centerOfRotationChaser ._value_changed .hasInterest ("set_centerOfRotationOffset__", this))
         {
            centerOfRotationOffset
               .assign (this .centerOfRotationChaser ._set_destination .getValue ())
               .add (centerOfRotationOffsetChange);

            this .centerOfRotationChaser ._set_destination = centerOfRotationOffset;
         }
         else
         {
            this .centerOfRotationChaser ._value_changed .addInterest ("set_centerOfRotationOffset__", this);

            centerOfRotationOffset
               .assign (viewpoint ._centerOfRotationOffset .getValue ())
               .add (centerOfRotationOffsetChange);

            this .centerOfRotationChaser ._set_value       = viewpoint ._centerOfRotationOffset;
            this .centerOfRotationChaser ._set_destination = centerOfRotationOffset;
         }
      };
   })(),
   getPositionOffset: (() =>
   {
      const
         distance = new Numbers_Vector3 (),
         d        = new Numbers_Vector3 (),
         oob      = new Numbers_Rotation4 ();

      return function (positionOffsetBefore, orientationOffsetBefore, orientationOffsetAfter)
      {
         this .getDistanceToCenter (distance, positionOffsetBefore);

         return (oob
            .assign (orientationOffsetBefore)
            .inverse ()
            .multRight (orientationOffsetAfter)
            .multVecRot (d .assign (distance))
            .subtract (distance)
            .add (positionOffsetBefore));
      };
   })(),
   getOrientationOffset: (() =>
   {
      const
         userOrientation   = new Numbers_Rotation4 (),
         orientationOffset = new Numbers_Rotation4 ();

      return function (roll, rotation, orientationOffsetBefore)
      {
         const
            viewpoint         = this .getActiveViewpoint (),
            straightenHorizon = this .getStraightenHorizon ();

         if (straightenHorizon)
         {
            // Determine userOrientation.

            userOrientation
               .assign (roll)
               .multRight (this .startOrientation)
               .multRight (rotation);

            // Straighten horizon of userOrientation.

            viewpoint .straightenHorizon (userOrientation);
         }
         else
         {
            userOrientation
               .assign (rotation)
               .multRight (viewpoint .getOrientation ())
               .multRight (orientationOffsetBefore);
         }

         const orientationOffsetAfter = orientationOffset
            .assign (viewpoint .getOrientation ())
            .inverse ()
            .multRight (userOrientation);

         return orientationOffsetAfter;
      };
   })(),
   getHorizonRotation: (() =>
   {
      const
         V = new Numbers_Vector3 (),
         N = new Numbers_Vector3 (),
         H = new Numbers_Vector3 (),
         r = new Numbers_Rotation4 ();

      return function (rotation)
      {
         rotation .multVecRot (V .assign (Numbers_Vector3 .Z_AXIS));
         N .assign (Numbers_Vector3 .Y_AXIS) .cross (V);
         H .assign (N) .cross (Numbers_Vector3 .Y_AXIS);
         r .setVectors (Numbers_Vector3 .Z_AXIS, H);

         return r;
      };
   })(),
   set_positionOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._positionOffset = value;
   },
   set_centerOfRotationOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._centerOfRotationOffset = value;
   },
   set_rotation__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._orientationOffset = this .getOrientationOffset (this .roll, value .getValue (), this .initialOrientationOffset);
      viewpoint ._positionOffset    = this .getPositionOffset (this .initialPositionOffset, this .initialOrientationOffset, viewpoint ._orientationOffset .getValue ());
   },
   disconnect ()
   {
      this .positionChaser         ._value_changed .removeInterest ("set_positionOffset__",         this);
      this .centerOfRotationChaser ._value_changed .removeInterest ("set_centerOfRotationOffset__", this);
      this .rotationChaser         ._value_changed .removeInterest ("set_rotation__",               this);

      this .removeSpinning ();
   },
   dispose ()
   {
      const browser = this .getBrowser ();

      this .disconnect ();
      this    .getNavigationInfo () ._transitionStart   .removeInterest ("disconnect", this);
      browser .getBrowserOptions () ._StraightenHorizon .removeInterest ("disconnect", this);

      browser ._activeViewpoint .removeInterest ("set_activeViewpoint__", this);

      browser .getSurface () .off (".ExamineViewer");
      ExamineViewer_$(document) .off (".ExamineViewer" + this .getId ());
   },
});

Object .defineProperties (ExamineViewer,
{
   typeName:
   {
      value: "ExamineViewer",
      enumerable: true,
   },
});

const ExamineViewer_default_ = ExamineViewer;
;

/* harmony default export */ const Navigation_ExamineViewer = (Namespace/* default */.A .add ("ExamineViewer", ExamineViewer_default_));
;// ./src/x_ite/Browser/Rendering/ScreenLine.js









function ScreenLine (browser, fromWidth, toWidth, tipStart)
{
   const gl = browser .getContext ();

   this .browser           = browser;
   this .indexBuffer       = gl .createBuffer ();
   this .colorBuffer       = gl .createBuffer ();
   this .vertexBuffer      = gl .createBuffer ();
   this .vertexArrayObject = new Rendering_VertexArray (gl);
   this .colorArray        = new Float32Array ([
      0, 0, 0, 1,  0, 0, 0, 1,  0, 0, 0, 1,  0, 0, 0, 1,  0, 0, 0, 0,  0, 0, 0, 0, // black
      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 0,  1, 1, 1, 0, // white
   ]);
   this .vertexArray       = new Float32Array (12 * 4) .fill (1);

   this .geometryContext   = new Rendering_GeometryContext ({
      renderObject: new Layering_Layer (browser .getPrivateScene ()),
      alphaMode: Shape_AlphaMode .BLEND,
      geometryType: 3,
      colorMaterial: true,
   });

   this .geometryContext .renderObject .setup ();

   gl .bindBuffer (gl .ELEMENT_ARRAY_BUFFER, this .indexBuffer);
   gl .bufferData (gl .ELEMENT_ARRAY_BUFFER, new Uint8Array ([
      0, 1, 3,  0, 3, 2,  2, 3,  5,  2,  5,  4, // black
      6, 7, 9,  6, 9, 8,  8, 9, 11,  8, 11, 10, // white
   ]), gl .STATIC_DRAW);

   this .setColor (Numbers_Color3 .WHITE);

   // Set black and white line quad vertices.

   const
      fromPoint   = new Numbers_Vector3 (),
      toPoint     = new Numbers_Vector3 (1, 0, 0),
      midPoint    = new Numbers_Vector3 (),
      normal      = new Numbers_Vector3 (),
      fromNormal  = new Numbers_Vector3 (),
      toNormal    = new Numbers_Vector3 (),
      vertex      = new Numbers_Vector3 (),
      vertexArray = this .vertexArray;

   midPoint .assign (fromPoint) .lerp (toPoint, tipStart);

   normal .assign (toPoint)
      .subtract (fromPoint)
      .normalize ()
      .multiply (0.5)
      .set (-normal .y, normal .x, 0);

   fromNormal .assign (normal) .multiply (fromWidth + 1);
   toNormal   .assign (normal) .multiply (toWidth   + 1);

   vertexArray .set (vertex .assign (fromPoint) .add (fromNormal),      0);
   vertexArray .set (vertex .assign (fromPoint) .subtract (fromNormal), 4);
   vertexArray .set (vertex .assign (midPoint)  .add (toNormal),        8);
   vertexArray .set (vertex .assign (midPoint)  .subtract (toNormal),   12);
   vertexArray .set (vertex .assign (toPoint)   .add (toNormal),        16);
   vertexArray .set (vertex .assign (toPoint)   .subtract (toNormal),   20);

   // Set line quad vertices.

   fromNormal .assign (normal) .multiply (fromWidth);
   toNormal   .assign (normal) .multiply (toWidth);

   vertexArray .set (vertex .assign (fromPoint) .add (fromNormal),      24);
   vertexArray .set (vertex .assign (fromPoint) .subtract (fromNormal), 28);
   vertexArray .set (vertex .assign (midPoint)  .add (toNormal),        32);
   vertexArray .set (vertex .assign (midPoint)  .subtract (toNormal),   36);
   vertexArray .set (vertex .assign (toPoint)   .add (toNormal),        40);
   vertexArray .set (vertex .assign (toPoint)   .subtract (toNormal),   44);

   // Transfer line.

   gl .bindBuffer (gl .ARRAY_BUFFER, this .vertexBuffer);
   gl .bufferData (gl .ARRAY_BUFFER, vertexArray, gl .STATIC_DRAW);
}

Object .assign (ScreenLine .prototype,
{
   setColor (color)
   {
      const
         browser        = this .browser,
         gl             = browser .getContext (),
         colorArray = this .colorArray;

      for (let i = 0; i < 6; ++ i)
         colorArray .set (color, 24 + i * 4);

      gl .bindBuffer (gl .ARRAY_BUFFER, this .colorBuffer);
      gl .bufferData (gl .ARRAY_BUFFER, colorArray, gl .STATIC_DRAW);

      return this;
   },
   display: (() =>
   {
      const
         xAxis                 = new Numbers_Vector3 (),
         yAxis                 = new Numbers_Vector3 (),
         projectionMatrix      = new Numbers_Matrix4 (),
         projectionMatrixArray = new Float32Array (Numbers_Matrix4 .IDENTITY),
         modelViewMatrixArray  = new Float32Array (Numbers_Matrix4 .IDENTITY),
         identity              = new Float32Array (Numbers_Matrix4 .IDENTITY),
         clipPlanes            = [ ];

      return function (fromPoint, toPoint, frameBuffer)
      {
         // Configure HUD

         const
            browser      = this .browser,
            gl           = browser .getContext (),
            viewport     = browser .getViewport (),
            width        = viewport [2],
            height       = viewport [3],
            contentScale = browser .getRenderingProperty ("ContentScale");

         frameBuffer .bind ();

         // Set viewport.

         gl .viewport (... viewport);
         gl .scissor (... viewport);
         gl .blendFuncSeparate (gl .SRC_ALPHA, gl .ONE_MINUS_SRC_ALPHA, gl .ONE, gl .ONE_MINUS_SRC_ALPHA);

         // Set projection and model view matrix.

         projectionMatrixArray .set (Geometry_Camera .ortho (0, width, 0, height, -1, 1, projectionMatrix));

         xAxis .assign (toPoint) .subtract (fromPoint);
         yAxis .set (-xAxis .y, xAxis .x, 0) .normalize () .multiply (contentScale);

         modelViewMatrixArray .set (xAxis,      0);
         modelViewMatrixArray .set (yAxis,      4);
         modelViewMatrixArray .set (fromPoint, 12);

         // Set uniforms and attributes.

         const shaderNode = browser .getDefaultMaterial () .getShader (this .geometryContext);

         shaderNode .enable (gl);
         shaderNode .setClipPlanes (gl, clipPlanes);

         gl .uniformMatrix4fv (shaderNode .x3d_ProjectionMatrix, false, projectionMatrixArray);
         gl .uniformMatrix4fv (shaderNode .x3d_ModelViewMatrix,  false, modelViewMatrixArray);
         gl .uniformMatrix4fv (shaderNode .x3d_EyeMatrix,        false, identity);
         gl .uniform1f        (shaderNode .x3d_Transparency, 0);

         if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
         {
            gl .bindBuffer (gl .ELEMENT_ARRAY_BUFFER, this .indexBuffer);

            shaderNode .enableColorAttribute  (gl, this .colorBuffer,  0, 0);
            shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
         }

         // Draw a black and a white line.

         gl .depthMask (false);
         gl .disable (gl .DEPTH_TEST);
         gl .enable (gl .BLEND);
         gl .enable (gl .CULL_FACE);
         gl .frontFace (gl .CCW);
         gl .drawElements (gl .TRIANGLES, 24, gl .UNSIGNED_BYTE, 0);
         gl .depthMask (true);
         gl .enable (gl .DEPTH_TEST);
         gl .disable (gl .BLEND);
      };
   })(),
   dispose ()
   {
      const gl = this .browser .getContext ();

      gl .deleteBuffer (this .vertexBuffer);
      this .vertexArrayObject .dispose (gl);
   },
});

const ScreenLine_default_ = ScreenLine;
;

/* harmony default export */ const Rendering_ScreenLine = (Namespace/* default */.A .add ("ScreenLine", ScreenLine_default_));
;// ./src/x_ite/Browser/Navigation/X3DFlyViewer.js
/* provided dependency */ var X3DFlyViewer_jquery_mousewheel = __webpack_require__(395);
/* provided dependency */ var X3DFlyViewer_$ = __webpack_require__(44).A;






void (typeof X3DFlyViewer_jquery_mousewheel); // import plugin

const X3DFlyViewer_macOS = /Mac OS X/i .test (navigator .userAgent)

const
   SPEED_FACTOR           = 0.007,
   SHIFT_SPEED_FACTOR     = 4 * SPEED_FACTOR,
   ROTATION_SPEED_FACTOR  = 1.4,
   ROTATION_LIMIT         = 40,
   PAN_SPEED_FACTOR       = SPEED_FACTOR,
   PAN_SHIFT_SPEED_FACTOR = 1.4 * PAN_SPEED_FACTOR,
   ROLL_ANGLE             = X3DFlyViewer_macOS ? Math .PI / 512 : Math .PI / 32,
   X3DFlyViewer_ROTATE_TIME            = 0.3;

const
   MOVE = 0,
   PAN  = 1;

function X3DFlyViewer (executionContext, navigationInfo)
{
   Navigation_X3DViewer .call (this, executionContext, navigationInfo);

   const browser = this .getBrowser ();

   this .button            = -1;
   this .fromVector        = new Numbers_Vector3 ();
   this .toVector          = new Numbers_Vector3 ();
   this .direction         = new Numbers_Vector3 ();
   this .startTime         = 0;
   this .event             = null;
   this .lookAround        = false;
   this .orientationChaser = new Followers_OrientationChaser (executionContext);
   this .rubberBand        = new Rendering_ScreenLine (browser, 1, 1, 0.4);
}

Object .assign (Object .setPrototypeOf (X3DFlyViewer .prototype, Navigation_X3DViewer .prototype),
{
   initialize ()
   {
      Navigation_X3DViewer .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      // Bind pointing device events.

      element .on ("mousedown.X3DFlyViewer",  this .mousedown  .bind (this));
      element .on ("mouseup.X3DFlyViewer",    this .mouseup    .bind (this));
      element .on ("mousewheel.X3DFlyViewer", this .mousewheel .bind (this));

      element .on ("touchstart.X3DFlyViewer", this .touchstart .bind (this));
      element .on ("touchend.X3DFlyViewer",   this .touchend   .bind (this));

      browser ._controlKey .addInterest ("set_controlKey__", this);

      // Setup look around chaser.

      this .orientationChaser ._duration = X3DFlyViewer_ROTATE_TIME;
      this .orientationChaser .setup ();
   },
   set_controlKey__ ()
   {
      if (this .event && this .event .button === 0)
      {
         this .button = -1;
         this .mousedown (this .event);
      }
   },
   mousedown (event)
   {
      if (this .button >= 0)
         return;

      this .event = event;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      switch (this .getButton (event .button))
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Start walk or fly.

            this .button = event .button;

            X3DFlyViewer_$(document) .on ("mouseup.X3DFlyViewer"   + this .getId (), this .mouseup   .bind (this));
            X3DFlyViewer_$(document) .on ("mousemove.X3DFlyViewer" + this .getId (), this .mousemove .bind (this));
            X3DFlyViewer_$(document) .on ("touchend.X3DFlyViewer"  + this .getId (), this .touchend  .bind (this));
            X3DFlyViewer_$(document) .on ("touchmove.X3DFlyViewer" + this .getId (), this .touchmove .bind (this));

            this .disconnect ();
            this .getActiveViewpoint () .transitionStop ();
            this .getBrowser () .setCursor ("MOVE");

            if (this .getBrowser () .getControlKey () || this .getBrowser () .getCommandKey () || this .lookAround)
            {
               // Look around.

               this .trackballProjectToSphere (x, y, this .fromVector);
            }
            else
            {
               // Move.

               this .fromVector .set (x, 0, -y);
               this .toVector   .assign (this .fromVector);

               this .getFlyDirection (this .fromVector, this .toVector, this .direction);
               this .addFly ();

               if (this .getBrowser () .getBrowserOption ("Rubberband"))
                  this .getBrowser () .finishedEvents () .addInterest ("display", this, MOVE);
            }

            this ._isActive = true;
            break;
         }
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Start pan.

            this .button = event .button;

            X3DFlyViewer_$(document) .on ("mouseup.X3DFlyViewer"   + this .getId (), this .mouseup   .bind (this));
            X3DFlyViewer_$(document) .on ("mousemove.X3DFlyViewer" + this .getId (), this .mousemove .bind (this));

            this .disconnect ();
            this .getActiveViewpoint () .transitionStop ();
            this .getBrowser () .setCursor ("MOVE");

            this .fromVector .set (x, y, 0);
            this .toVector   .assign (this .fromVector);
            this .direction  .set (0);

            this .addPan ();

            if (this .getBrowser () .getBrowserOption ("Rubberband"))
               this .getBrowser () .finishedEvents () .addInterest ("display", this, PAN);

            this ._isActive = true;
            break;
         }
      }
   },
   mouseup (event)
   {
      if (event .button !== this .button)
         return;

      // Stop event propagation.

      event .preventDefault ();

      // Disable all.

      this .event  = null;
      this .button = -1;

      this .direction .set (0);

      X3DFlyViewer_$(document) .off (".X3DFlyViewer" + this .getId ());

      this .disconnect ();
      this .getBrowser () .setCursor ("DEFAULT");

      this ._isActive = false;
   },
   mousemove (event)
   {
      const browser = this .getBrowser ();

      browser .addBrowserEvent ();

      this .event = event;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      switch (this .getButton (this .button))
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            if (browser .getControlKey () || browser .getCommandKey () || this .lookAround)
            {
               // Look around

               const toVector = this .trackballProjectToSphere (x, y, this .toVector);

               this .addRotation (this .fromVector, toVector);
               this .fromVector .assign (toVector);
               break;
            }
            else
            {
               // Fly

               this .toVector .set (x, 0, -y);
               this .getFlyDirection (this .fromVector, this .toVector, this .direction);
               this .direction .divide (browser .getRenderingProperty ("ContentScale"));
               break;
            }
         }
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Pan

            this .toVector  .set (x, y, 0);
            this .direction .assign (this .toVector) .subtract (this .fromVector);
            this .direction .divide (browser .getRenderingProperty ("ContentScale"));
            break;
         }
      }
   },
   mousewheel (event)
   {
      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      // Stop event propagation.

      event .preventDefault ();

      // Change viewpoint position.

      const viewpoint = this .getActiveViewpoint ();

      viewpoint .transitionStop ();

      if (event .deltaY > 0)
         this .addRoll (-ROLL_ANGLE);

      else if (event .deltaY < 0)
         this .addRoll (ROLL_ANGLE);
   },
   touchstart (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // Start fly or walk (button 0).

            event .button = 0;
            event .pageX  = touches [0] .pageX;
            event .pageY  = touches [0] .pageY;

            this .mousedown (event);
            break;
         }
         case 2:
         {
            // End fly or walk (button 0).

            this .touchend (event);

            // Start look around (button 0).

            this .lookAround = true;
            event .button    = 0;
            event .pageX     = (touches [0] .pageX + touches [1] .pageX) / 2;
            event .pageY     = (touches [0] .pageY + touches [1] .pageY) / 2;

            this .mousedown (event);
            break;
         }
         case 3:
         {
            // End look around (button 0).

            this .touchend (event);
            break;
         }
      }
   },
   touchend (event)
   {
      switch (this .button)
      {
         case 0:
         {
            // End move or look around (button 0).
            this .lookAround = false;
            event .button    = 0;

            this .mouseup (event);
            break;
         }
      }
   },
   touchmove (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // Fly or walk (button 0).

            event .button = 0;
            event .pageX  = touches [0] .pageX;
            event .pageY  = touches [0] .pageY;

            this .mousemove (event);
            break;
         }
         case 2:
         {
            // Fly or walk (button 0).

            event .button = 0;
            event .pageX  = (touches [0] .pageX + touches [1] .pageX) / 2;
            event .pageY  = (touches [0] .pageY + touches [1] .pageY) / 2;

            this .mousemove (event);
            break;
         }
      }
   },
   fly: (() =>
   {
      const
         upVector           = new Numbers_Vector3 (),
         direction          = new Numbers_Vector3 (),
         axis               = new Numbers_Vector3 (),
         userOrientation    = new Numbers_Rotation4 (),
         orientationOffset  = new Numbers_Rotation4 (),
         rubberBandRotation = new Numbers_Rotation4 ();

      return function ()
      {
         const
            navigationInfo = this .getNavigationInfo (),
            viewpoint      = this .getActiveViewpoint (),
            now            = Date .now (),
            dt             = (now - this .startTime) / 1000;

         upVector .assign (viewpoint .getUpVector ());

         // Rubberband values

         if (this .direction .z > 0)
            rubberBandRotation .setVectors (this .direction, axis .set (0, 0, 1));
         else
            rubberBandRotation .setVectors (axis .set (0, 0, -1), this .direction);

         const rubberBandLength = this .direction .norm ();

         // Determine positionOffset.

         let speedFactor = 1 - rubberBandRotation .angle / (Math .PI / 2);

         speedFactor *= navigationInfo ._speed .getValue ();
         speedFactor *= viewpoint .getSpeedFactor ();
         speedFactor *= this .getBrowser () .getShiftKey () ? SHIFT_SPEED_FACTOR : SPEED_FACTOR;
         speedFactor *= dt;

         const
            translation = this .getTranslationOffset (direction .assign (this .direction) .multiply (speedFactor)),
            constrained = this .getActiveLayer () .constrainTranslation (translation);

         viewpoint ._positionOffset = constrained .add (viewpoint ._positionOffset .getValue ());

         // Determine weight for rubberBandRotation.

         const weight = ROTATION_SPEED_FACTOR * dt * (rubberBandLength / (rubberBandLength + ROTATION_LIMIT)) ** 2;

         // Determine userOrientation.

         userOrientation
            .assign (Numbers_Rotation4 .IDENTITY)
            .slerp (rubberBandRotation, weight)
            .multRight (viewpoint .getUserOrientation ());

         // Straighten horizon of userOrientation.

         if (this .getStraightenHorizon ())
            viewpoint .straightenHorizon (userOrientation);

         // Determine orientationOffset.

         orientationOffset
            .assign (viewpoint .getOrientation ())
            .inverse ()
            .multRight (userOrientation);

         // Set orientationOffset.

         viewpoint ._orientationOffset = orientationOffset;

         this .startTime = now;
      };
   })(),
   pan: (() =>
   {
      const
         direction = new Numbers_Vector3 (),
         axis      = new Numbers_Vector3 ();

      return function ()
      {
         const
            navigationInfo = this .getNavigationInfo (),
            viewpoint      = this .getActiveViewpoint (),
            now            = Date .now (),
            dt             = (now - this .startTime) / 1000,
            upVector       = viewpoint .getUpVector ();

         this .constrainPanDirection (direction .assign (this .direction));

         let speedFactor = 1;

         speedFactor *= navigationInfo ._speed .getValue ();
         speedFactor *= viewpoint .getSpeedFactor ();
         speedFactor *= this .getBrowser () .getShiftKey () ? PAN_SHIFT_SPEED_FACTOR : PAN_SPEED_FACTOR;
         speedFactor *= dt;

         const
            orientation = viewpoint .getUserOrientation () .multRight (Numbers_Rotation4 .fromVectors (viewpoint .getUserOrientation () .multVecRot (axis .assign (Numbers_Vector3 .Y_AXIS)), upVector)),
            translation = orientation .multVecRot (direction .multiply (speedFactor)),
            constrained = this .getActiveLayer () .constrainTranslation (translation);

         viewpoint ._positionOffset = constrained .add (viewpoint ._positionOffset .getValue ());

         this .startTime = now;
      };
   })(),
   set_orientationOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._orientationOffset = value;
   },
   addFly ()
   {
      if (this .startTime)
         return;

      this .getBrowser () .prepareEvents () .addInterest ("fly", this);
      this .getBrowser () .addBrowserEvent ();

      this .startTime = Date .now ();
   },
   addPan ()
   {
      if (this .startTime)
         return;

      this .disconnect ();
      this .getBrowser () .prepareEvents () .addInterest ("pan", this);
      this .getBrowser () .addBrowserEvent ();

      this .startTime = Date .now ();
   },
   addRoll: (() =>
   {
      const
         orientationOffset = new Numbers_Rotation4 (),
         roll              = new Numbers_Rotation4 ();

      return function (rollAngle)
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .orientationChaser ._isActive .getValue () && this .orientationChaser ._value_changed .hasInterest ("set_orientationOffset__", this))
         {
            orientationOffset
               .assign (viewpoint .getOrientation ())
               .inverse ()
               .multRight (roll .set (1, 0, 0, rollAngle))
               .multRight (viewpoint .getOrientation ())
               .multRight (this .orientationChaser ._set_destination .getValue ());

            this .orientationChaser ._set_destination = orientationOffset;
         }
         else
         {
            orientationOffset
               .assign (viewpoint .getOrientation ())
               .inverse ()
               .multRight (roll .set (1, 0, 0, rollAngle))
               .multRight (viewpoint .getUserOrientation ());

            this .orientationChaser ._set_value       = viewpoint ._orientationOffset;
            this .orientationChaser ._set_destination = orientationOffset;
         }

         this .disconnect ();
         this .orientationChaser ._value_changed .addInterest ("set_orientationOffset__", this);
      };
   })(),
   addRotation: (() =>
   {
      const
         userOrientation   = new Numbers_Rotation4 (),
         orientationOffset = new Numbers_Rotation4 ();

      return function (fromVector, toVector)
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .orientationChaser ._isActive .getValue () && this .orientationChaser ._value_changed .hasInterest ("set_orientationOffset__", this))
         {
            userOrientation
               .setVectors (toVector, fromVector)
               .multRight (viewpoint .getOrientation ())
               .multRight (this .orientationChaser ._set_destination .getValue ());

            if (this .getStraightenHorizon ())
               viewpoint .straightenHorizon (userOrientation);

            orientationOffset .assign (viewpoint .getOrientation ()) .inverse () .multRight (userOrientation);

            this .orientationChaser ._set_destination = orientationOffset;
         }
         else
         {
            userOrientation
               .setVectors (toVector, fromVector)
               .multRight (viewpoint .getUserOrientation ());

            if (this .getStraightenHorizon ())
               viewpoint .straightenHorizon (userOrientation);

            orientationOffset .assign (viewpoint .getOrientation ()) .inverse () .multRight (userOrientation);

            this .orientationChaser ._set_value       = viewpoint ._orientationOffset;
            this .orientationChaser ._set_destination = orientationOffset;
         }

         this .disconnect ();
         this .orientationChaser ._value_changed .addInterest ("set_orientationOffset__", this);
      };
   })(),
   display: (() =>
   {
      const
         fromPoint = new Numbers_Vector3 (),
         toPoint   = new Numbers_Vector3 ();

      return function (type)
      {
         // Configure HUD

         const browser = this .getBrowser ();

         // Display Rubberband.

         switch (type)
         {
            case MOVE:
            {
               fromPoint .set (this .fromVector .x, -this .fromVector .z, 0);
               toPoint   .set (this .toVector   .x, -this .toVector   .z, 0);
               break;
            }
            case PAN:
            {
               fromPoint .set (this .fromVector .x, this .fromVector .y, 0);
               toPoint   .set (this .toVector   .x, this .toVector   .y, 0);
               break;
            }
         }

         for (const frameBuffer of browser .getFramebuffers ())
            this .rubberBand .display (fromPoint, toPoint, frameBuffer);
      };
   })(),
   disconnect ()
   {
      const browser = this .getBrowser ();

      browser .addBrowserEvent ();

      browser .prepareEvents ()  .removeInterest ("fly", this);
      browser .prepareEvents ()  .removeInterest ("pan", this);
      browser .finishedEvents () .removeInterest ("display", this);

      this .orientationChaser ._value_changed .removeInterest ("set_orientationOffset__", this);

      this .startTime = 0;
   },
   dispose ()
   {
      this .rubberBand .dispose ();

      this .disconnect ();
      this .getBrowser () ._controlKey .removeInterest ("set_controlKey__", this);
      this .getBrowser () .getSurface () .off (".X3DFlyViewer");
      X3DFlyViewer_$(document) .off (".X3DFlyViewer" + this .getId ());
   },
});

const X3DFlyViewer_default_ = X3DFlyViewer;
;

/* harmony default export */ const Navigation_X3DFlyViewer = (Namespace/* default */.A .add ("X3DFlyViewer", X3DFlyViewer_default_));
;// ./src/x_ite/Browser/Navigation/WalkViewer.js




function WalkViewer (executionContext, navigationInfo)
{
   Navigation_X3DFlyViewer .call (this, executionContext, navigationInfo);
}

Object .assign (Object .setPrototypeOf (WalkViewer .prototype, Navigation_X3DFlyViewer .prototype),
{
   isCollidable ()
   {
      return true;
   },
   getStraightenHorizon ()
   {
      return true;
   },
   getFlyDirection (fromVector, toVector, direction)
   {
      return direction .assign (toVector) .subtract (fromVector);
   },
   getTranslationOffset: (() =>
   {
      const
         localYAxis      = new Numbers_Vector3 (),
         userOrientation = new Numbers_Rotation4 (),
         rotation        = new Numbers_Rotation4 ();

      return function (velocity)
      {
         const
            viewpoint = this .getActiveViewpoint (),
            upVector  = viewpoint .getUpVector ();

         userOrientation .assign (viewpoint .getUserOrientation ());
         userOrientation .multVecRot (localYAxis .assign (Numbers_Vector3 .Y_AXIS));
         rotation        .setVectors (localYAxis, upVector);

         const orientation = userOrientation .multRight (rotation);

         return orientation .multVecRot (velocity);
      };
   })(),
   constrainPanDirection (direction)
   {
      if (direction .y < 0)
         direction .y = 0;

      return direction;
   },
});

Object .defineProperties (WalkViewer,
{
   typeName:
   {
      value: "WalkViewer",
      enumerable: true,
   },
});

const WalkViewer_default_ = WalkViewer;
;

/* harmony default export */ const Navigation_WalkViewer = (Namespace/* default */.A .add ("WalkViewer", WalkViewer_default_));
;// ./src/x_ite/Browser/Navigation/FlyViewer.js


function FlyViewer (executionContext, navigationInfo)
{
   Navigation_X3DFlyViewer .call (this, executionContext, navigationInfo);
}

Object .assign (Object .setPrototypeOf (FlyViewer .prototype, Navigation_X3DFlyViewer .prototype),
{
   isCollidable ()
   {
      return this .isActive ();
   },
   getFlyDirection (fromVector, toVector, direction)
   {
      return direction .assign (toVector) .subtract (fromVector);
   },
   getTranslationOffset (velocity)
   {
      return this .getActiveViewpoint () .getUserOrientation () .multVecRot (velocity);
   },
   constrainPanDirection (direction)
   {
      return direction;
   },
});

Object .defineProperties (FlyViewer,
{
   typeName:
   {
      value: "FlyViewer",
      enumerable: true,
   },
});

const FlyViewer_default_ = FlyViewer;
;

/* harmony default export */ const Navigation_FlyViewer = (Namespace/* default */.A .add ("FlyViewer", FlyViewer_default_));
;// ./src/x_ite/Browser/Navigation/PlaneViewer.js
/* provided dependency */ var PlaneViewer_jquery_mousewheel = __webpack_require__(395);
/* provided dependency */ var PlaneViewer_$ = __webpack_require__(44).A;



void (typeof PlaneViewer_jquery_mousewheel); // import plugin

const PlaneViewer_macOS = /Mac OS X/i .test (navigator .userAgent)

const PlaneViewer_SCROLL_FACTOR = PlaneViewer_macOS ? 1 / 160 : 1 / 20;

const
   vector                 = new Numbers_Vector3 (),
   positionOffset         = new Numbers_Vector3 (),
   centerOfRotationOffset = new Numbers_Vector3 ();

function PlaneViewer (executionContext, navigationInfo)
{
   Navigation_X3DViewer .call (this, executionContext, navigationInfo);

   this .button    = -1;
   this .fromPoint = new Numbers_Vector3 ();
   this .toPoint   = new Numbers_Vector3 ();
}

Object .assign (Object .setPrototypeOf (PlaneViewer .prototype, Navigation_X3DViewer .prototype),
{
   initialize ()
   {
      Navigation_X3DViewer .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      element .on ("mousedown.PlaneViewer",  this .mousedown  .bind (this));
      element .on ("mouseup.PlaneViewer",    this .mouseup    .bind (this));
      element .on ("mousemove.PlaneViewer",  this .mousemove  .bind (this));
      element .on ("mousewheel.PlaneViewer", this .mousewheel .bind (this));
   },
   mousedown (event)
   {
      if (this .button >= 0)
         return;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      switch (this .getButton (event .button))
      {
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Start move.

            this .button = event .button;

            this .getBrowser () .getSurface () .off ("mousemove.PlaneViewer");
            PlaneViewer_$(document) .on ("mouseup.PlaneViewer"   + this .getId (), this .mouseup .bind (this));
            PlaneViewer_$(document) .on ("mousemove.PlaneViewer" + this .getId (), this .mousemove .bind (this));

            this .getActiveViewpoint () .transitionStop ();
            this .getBrowser () .setCursor ("MOVE");

            this .getPointOnCenterPlane (x, y, this .fromPoint);

            this ._isActive = true;
            break;
         }
      }
   },
   mouseup (event)
   {
      if (event .button !== this .button)
         return;

      // Stop event propagation.

      event .preventDefault ();

      // Disable all.

      this .button = -1;

      PlaneViewer_$(document) .off (".PlaneViewer" + this .getId ());
      this .getBrowser () .getSurface () .on ("mousemove.PlaneViewer", this .mousemove .bind (this));

      this .getBrowser () .setCursor ("DEFAULT");

      this ._isActive = false;
   },
   mousemove (event)
   {
      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      switch (this .getButton (this .button))
      {
         case 1:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Move.

            const
               viewpoint   = this .getActiveViewpoint (),
               toPoint     = this .getPointOnCenterPlane (x, y, this .toPoint),
               translation = viewpoint .getUserOrientation () .multVecRot (this .fromPoint .subtract (toPoint));

            viewpoint ._positionOffset         = positionOffset         .assign (viewpoint ._positionOffset         .getValue ()) .add (translation);
            viewpoint ._centerOfRotationOffset = centerOfRotationOffset .assign (viewpoint ._centerOfRotationOffset .getValue ()) .add (translation);

            this .fromPoint .assign (toPoint);
            break;
         }
      }
   },
   mousewheel (event)
   {
      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      // Stop event propagation.

      event .preventDefault ();

      // Change viewpoint position.

      const
         viewpoint = this .getActiveViewpoint (),
         fromPoint = this .getPointOnCenterPlane (x, y, this .fromPoint);

      viewpoint .transitionStop ();

      if (event .deltaY > 0) // Move backwards.
      {
         viewpoint ._fieldOfViewScale = Math .max (0.00001, viewpoint ._fieldOfViewScale .getValue () * (1 - PlaneViewer_SCROLL_FACTOR));
      }
      else if (event .deltaY < 0) // Move forwards.
      {
         viewpoint ._fieldOfViewScale = viewpoint ._fieldOfViewScale .getValue () * (1 + PlaneViewer_SCROLL_FACTOR);

         this .constrainFieldOfViewScale ();
      }

      const
         toPoint     = this .getPointOnCenterPlane (x, y, this .toPoint),
         translation = viewpoint .getUserOrientation () .multVecRot (vector .assign (fromPoint) .subtract (toPoint));

      viewpoint ._positionOffset         = positionOffset         .assign (viewpoint ._positionOffset         .getValue ()) .add (translation);
      viewpoint ._centerOfRotationOffset = centerOfRotationOffset .assign (viewpoint ._centerOfRotationOffset .getValue ()) .add (translation);
   },
   constrainFieldOfViewScale ()
   {
      const viewpoint = this .getActiveViewpoint ();

      if (viewpoint .getTypeName () .match (/^(?:Viewpoint|GeoViewpoint)$/))
      {
         if (viewpoint ._fieldOfView .getValue () * viewpoint ._fieldOfViewScale .getValue () >= Math .PI)
            viewpoint ._fieldOfViewScale = (Math .PI - 0.001) / viewpoint ._fieldOfView .getValue ();
      }
   },
   dispose ()
   {
      this .getBrowser () .getSurface () .off (".PlaneViewer");
      PlaneViewer_$(document) .off (".PlaneViewer" + this .getId ());
   },
});

Object .defineProperties (PlaneViewer,
{
   typeName:
   {
      value: "PlaneViewer",
      enumerable: true,
   },
});

const PlaneViewer_default_ = PlaneViewer;
;

/* harmony default export */ const Navigation_PlaneViewer = (Namespace/* default */.A .add ("PlaneViewer", PlaneViewer_default_));
;// ./src/x_ite/Browser/Navigation/NoneViewer.js


function NoneViewer (executionContext, navigationInfo)
{
   Navigation_X3DViewer .call (this, executionContext, navigationInfo);
}

Object .setPrototypeOf (NoneViewer .prototype, Navigation_X3DViewer .prototype);

Object .defineProperties (NoneViewer,
{
   typeName:
   {
      value: "NoneViewer",
      enumerable: true,
   },
});

const NoneViewer_default_ = NoneViewer;
;

/* harmony default export */ const Navigation_NoneViewer = (Namespace/* default */.A .add ("NoneViewer", NoneViewer_default_));
;// ./src/x_ite/Browser/Navigation/LookAtViewer.js
/* provided dependency */ var LookAtViewer_jquery_mousewheel = __webpack_require__(395);
/* provided dependency */ var LookAtViewer_$ = __webpack_require__(44).A;







void (typeof LookAtViewer_jquery_mousewheel); // import plugin

const LookAtViewer_macOS = /Mac OS X/i .test (navigator .userAgent)

const
   LookAtViewer_SCROLL_FACTOR = LookAtViewer_macOS ? 1 / 120 : 1 / 20,
   LookAtViewer_MOVE_TIME     = 0.3,
   LookAtViewer_ROTATE_TIME   = 0.3;

function LookAtViewer (executionContext, navigationInfo)
{
   Navigation_X3DViewer .call (this, executionContext, navigationInfo);

   this .button                 = -1;
   this .fromVector             = new Numbers_Vector3 ();
   this .toVector               = new Numbers_Vector3 ();

   this .touch1                 = new Numbers_Vector2 ();
   this .touch2                 = new Numbers_Vector2 ();
   this .tapStart               = 0;
   this .dblTapInterval         = 0.4;

   this .positionChaser         = new Followers_PositionChaser (executionContext);
   this .centerOfRotationChaser = new Followers_PositionChaser (executionContext);
   this .orientationChaser      = new Followers_OrientationChaser (executionContext);
}

Object .assign (Object .setPrototypeOf (LookAtViewer .prototype, Navigation_X3DViewer .prototype),
{
   initialize ()
   {
      Navigation_X3DViewer .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      // Bind pointing device events.

      element .on ("mousedown.LookAtViewer",  this .mousedown  .bind (this));
      element .on ("mouseup.LookAtViewer",    this .mouseup    .bind (this));
      element .on ("dblclick.LookAtViewer",   this .dblclick   .bind (this));
      element .on ("mousewheel.LookAtViewer", this .mousewheel .bind (this));

      element .on ("touchstart.LookAtViewer", this .touchstart .bind (this));
      element .on ("touchend.LookAtViewer",   this .touchend   .bind (this));

      // Setup chaser.

      this .positionChaser ._duration = LookAtViewer_MOVE_TIME;
      this .positionChaser .setup ();

      this .centerOfRotationChaser ._duration = LookAtViewer_MOVE_TIME;
      this .centerOfRotationChaser .setup ();

      this .orientationChaser ._duration = LookAtViewer_ROTATE_TIME;
      this .orientationChaser .setup ();
   },
   mousedown (event)
   {
      if (this .button >= 0)
         return;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      if (!this .isPointerInRectangle (x, y))
         return;

      switch (event .button)
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Look around.

            this .button = event .button;

            LookAtViewer_$(document) .on ("mouseup.LookAtViewer"   + this .getId (), this .mouseup   .bind (this));
            LookAtViewer_$(document) .on ("mousemove.LookAtViewer" + this .getId (), this .mousemove .bind (this));
            LookAtViewer_$(document) .on ("touchend.LookAtViewer"  + this .getId (), this .mouseup   .bind (this));
            LookAtViewer_$(document) .on ("touchmove.LookAtViewer" + this .getId (), this .touchmove .bind (this));

            this .getActiveViewpoint () .transitionStop ();
            this .trackballProjectToSphere (x, y, this .fromVector);

            this ._isActive = true;
            break;
         }
      }
   },
   mouseup (event)
   {
      if (event .button !== this .button)
         return;

      this .button = -1;

      LookAtViewer_$(document) .off (".LookAtViewer" + this .getId ());

      switch (event .button)
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Look around.

            this ._isActive = false;
            break;
         }
      }
   },
   dblclick (event)
   {
      // Stop event propagation.

      event .preventDefault ();

      // Look at.

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      this .disconnect ();
      this .lookAtPoint (x, y, this .getStraightenHorizon ());
   },
   mousemove (event)
   {
      this .getBrowser () .addBrowserEvent ();

      this .event = event;

      const { x, y } = this .getBrowser () .getPointerFromEvent (event);

      switch (this .button)
      {
         case 0:
         {
            // Stop event propagation.

            event .preventDefault ();

            // Look around.

            const toVector = this .trackballProjectToSphere (x, y, this .toVector);

            this .addRotation (this .fromVector, toVector);
            this .fromVector .assign (toVector);
            break;
         }
      }
   },
   mousewheel: (() =>
   {
      const
         step        = new Numbers_Vector3 (),
         translation = new Numbers_Vector3 ();

      return function (event)
      {
         const { x, y } = this .getBrowser () .getPointerFromEvent (event);

         if (!this .isPointerInRectangle (x, y))
            return;

         // Stop event propagation.

         event .preventDefault ();

         // Change viewpoint position.

         const viewpoint = this .getActiveViewpoint ();

         viewpoint .transitionStop ();

         this .getDistanceToCenter (step) .multiply (event .zoomFactor || LookAtViewer_SCROLL_FACTOR),
         viewpoint .getUserOrientation () .multVecRot (translation .set (0, 0, step .norm ()));

         if (event .deltaY > 0)
            this .addMove (translation .negate (), Numbers_Vector3 .ZERO);

         else if (event .deltaY < 0)
            this .addMove (translation, Numbers_Vector3 .ZERO);
      };
   })(),
   touchstart (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // Start move (button 0).

            this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
            break;
         }
         case 2:
         {
            // End move (button 0).

            this .touchend (event);

            // Start look around (button 0).

            event .button = 0;
            event .pageX  = (touches [0] .pageX + touches [1] .pageX) / 2;
            event .pageY  = (touches [0] .pageY + touches [1] .pageY) / 2;

            this .mousedown (event);

            // Start zoom (mouse wheel).

            this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
            this .touch2 .set (touches [1] .pageX, touches [1] .pageY);
            break;
         }
         case 3:
         {
            this .touchend (event);
            break;
         }
      }
   },
   touchend (event)
   {
      switch (this .button)
      {
         case 0:
         {
            // End look around (button 0).
            this .mouseup (event);
            break;
         }
      }

      // Start dblclick (button 0).

      if (Date .now () - this .tapStart < this .dblTapInterval)
      {
         event .button = 1;
         event .pageX  = this .touch1 .x;
         event .pageY  = this .touch1 .y;

         this .dblclick (event);
      }

      this .tapStart = Date .now ();
   },
   touchmove: (() =>
   {
      const
         MOVE_ANGLE   = 0.7,
         ZOOM_ANGLE   = -0.7,
         touch1Change = new Numbers_Vector2 (),
         touch2Change = new Numbers_Vector2 ();

      return function (event)
      {
         const touches = event .originalEvent .touches;

         switch (touches .length)
         {
            case 1:
            {
               // Move (button 0).
               break;
            }
            case 2:
            {
               touch1Change .set (touches [0] .pageX, touches [0] .pageY) .subtract (this .touch1) .normalize ();
               touch2Change .set (touches [1] .pageX, touches [1] .pageY) .subtract (this .touch2) .normalize ();

               const
                  move = touch1Change .dot (touch2Change) > MOVE_ANGLE,
                  zoom = touch1Change .dot (touch2Change) < ZOOM_ANGLE;

               if (move)
               {
                  // Look around (button 0).

                  event .button = 0;
                  event .pageX  = (touches [0] .pageX + touches [1] .pageX) / 2;
                  event .pageY  = (touches [0] .pageY + touches [1] .pageY) / 2;

                  this .mousemove (event);
               }
               else if (zoom)
               {
                  // Zoom (mouse wheel).

                  const distance1 = this .touch1 .distance (this .touch2);

                  this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
                  this .touch2 .set (touches [1] .pageX, touches [1] .pageY);

                  const
                     distance2 = this .touch1 .distance (this .touch2),
                     delta     = distance2 - distance1;

                  event .deltaY     = delta;
                  event .zoomFactor = Math .abs (delta) / LookAtViewer_$(window) .width ();

                  event .pageX  = (touches [0] .pageX + touches [1] .pageX) / 2;
                  event .pageY  = (touches [0] .pageY + touches [1] .pageY) / 2;

                  this .mousewheel (event);
               }

               this .touch1 .set (touches [0] .pageX, touches [0] .pageY);
               this .touch2 .set (touches [1] .pageX, touches [1] .pageY);
               break;
            }
         }
      };
   })(),
   set_positionOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._positionOffset = value;
   },
   set_centerOfRotationOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._centerOfRotationOffset = value;
   },
   set_orientationOffset__ (value)
   {
      const viewpoint = this .getActiveViewpoint ();

      viewpoint ._orientationOffset = value;
   },
   addMove: (() =>
   {
      const
         positionOffset         = new Numbers_Vector3 (),
         centerOfRotationOffset = new Numbers_Vector3 ();

      return function (positionOffsetChange, centerOfRotationOffsetChange)
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .positionChaser ._isActive .getValue () && this .positionChaser ._value_changed .hasInterest ("set_positionOffset__", this))
         {
            positionOffset
               .assign (this .positionChaser ._set_destination .getValue ())
               .add (positionOffsetChange);

            this .positionChaser ._set_destination = positionOffset;
         }
         else
         {
            positionOffset
               .assign (viewpoint ._positionOffset .getValue ())
               .add (positionOffsetChange);

            this .positionChaser ._set_value       = viewpoint ._positionOffset;
            this .positionChaser ._set_destination = positionOffset;
         }

         if (this .centerOfRotationChaser ._isActive .getValue () && this .centerOfRotationChaser ._value_changed .hasInterest ("set_centerOfRotationOffset__", this))
         {
            centerOfRotationOffset
               .assign (this .centerOfRotationChaser ._set_destination .getValue ())
               .add (centerOfRotationOffsetChange);

            this .centerOfRotationChaser ._set_destination = centerOfRotationOffset;
         }
         else
         {
            centerOfRotationOffset
               .assign (viewpoint ._centerOfRotationOffset .getValue ())
               .add (centerOfRotationOffsetChange);

            this .centerOfRotationChaser ._set_value       = viewpoint ._centerOfRotationOffset;
            this .centerOfRotationChaser ._set_destination = centerOfRotationOffset;
         }

         this .disconnect ();
         this .positionChaser         ._value_changed .addInterest ("set_positionOffset__",         this);
         this .centerOfRotationChaser ._value_changed .addInterest ("set_centerOfRotationOffset__", this);
      };
   })(),
   addRotation: (() =>
   {
      const
         userOrientation   = new Numbers_Rotation4 (),
         orientationOffset = new Numbers_Rotation4 ();

      return function (fromVector, toVector)
      {
         const viewpoint = this .getActiveViewpoint ();

         if (this .orientationChaser ._isActive .getValue () && this .orientationChaser ._value_changed .hasInterest ("set_orientationOffset__", this))
         {
            userOrientation
               .setVectors (toVector, fromVector)
               .multRight (viewpoint .getOrientation ())
               .multRight (this .orientationChaser ._set_destination .getValue ());

            viewpoint .straightenHorizon (userOrientation);

            orientationOffset .assign (viewpoint .getOrientation ()) .inverse () .multRight (userOrientation);

            this .orientationChaser ._set_destination = orientationOffset;
         }
         else
         {
            userOrientation
               .setVectors (toVector, fromVector)
               .multRight (viewpoint .getUserOrientation ());

            viewpoint .straightenHorizon (userOrientation);

            orientationOffset .assign (viewpoint .getOrientation ()) .inverse () .multRight (userOrientation);

            this .orientationChaser ._set_value       = viewpoint ._orientationOffset;
            this .orientationChaser ._set_destination = orientationOffset;
         }

         this .disconnect ();
         this .orientationChaser ._value_changed .addInterest ("set_orientationOffset__", this);
      };
   })(),
   disconnect ()
   {
      this .orientationChaser      ._value_changed .removeInterest ("set_orientationOffset__", this);
      this .positionChaser         ._value_changed .removeInterest ("set_positionOffset__",         this)
      this .centerOfRotationChaser ._value_changed .removeInterest ("set_centerOfRotationOffset__", this)
   },
   dispose ()
   {
      this .getBrowser () .getSurface () .off (".LookAtViewer");
      LookAtViewer_$(document) .off (".LookAtViewer" + this .getId ());
   },
});

Object .defineProperties (LookAtViewer,
{
   typeName:
   {
      value: "LookAtViewer",
      enumerable: true,
   },
});

const LookAtViewer_default_ = LookAtViewer;
;

/* harmony default export */ const Navigation_LookAtViewer = (Namespace/* default */.A .add ("LookAtViewer", LookAtViewer_default_));
;// ./src/x_ite/Components/Lighting/X3DLightNode.js






function X3DLightNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DLightNode);

   // Private properties

   this .enabled = true;
}

Object .assign (Object .setPrototypeOf (X3DLightNode .prototype, Core_X3DChildNode .prototype),
{
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this ._global    .addInterest ("set_global__", this);
      this ._on        .addInterest ("set_on__",     this);
      this ._intensity .addInterest ("set_on__",     this);

      this .set_global__ ();
      this .set_on__ ();
   },
   set_global__ ()
   {
      this .setVisibleObject (this ._global .getValue ());
   },
   set_on__ ()
   {
      if (this ._on .getValue () && this .getIntensity () > 0 && this .enabled)
      {
         delete this .push;
         delete this .pop;
      }
      else
      {
         this .push = Function .prototype;
         this .pop  = Function .prototype;
      }
   },
   getLightKey ()
   {
      return 1; // [ClipPlane 0, X3DLightNode 1, EnvironmentLight 2, X3DTextureProjectorNode 3]
   },
   getEnabled ()
   {
      return this .enabled;
   },
   setEnabled (value)
   {
      this .enabled = value;
   },
   getGlobal ()
   {
      return this ._global .getValue ();
   },
   getColor ()
   {
      return this ._color .getValue ();
   },
   getIntensity ()
   {
      return Math .max (this ._intensity .getValue (), 0);
   },
   getAmbientIntensity ()
   {
      return Math_Algorithm .clamp (this ._ambientIntensity .getValue (), 0, 1);
   },
   getDirection ()
   {
      return this ._direction .getValue ();
   },
   getShadows ()
   {
      return this ._shadows .getValue ();
   },
   getShadowColor ()
   {
      return this ._shadowColor .getValue ();
   },
   getShadowIntensity ()
   {
      return this .getShadows () ? Math_Algorithm .clamp (this ._shadowIntensity .getValue (), 0, 1) : 0;
   },
   getShadowBias ()
   {
      return Math_Algorithm .clamp (this ._shadowBias .getValue (), 0, 1);
   },
   getShadowMapSize ()
   {
      return Math_Algorithm .clamp (this ._shadowMapSize .getValue (), 0, this .getBrowser () .getMaxTextureSize ());
   },
   getBiasMatrix: (() =>
   {
      // Transforms normalized coords from range (-1, 1) to (0, 1).
      const biasMatrix = new Numbers_Matrix4 (0.5, 0.0, 0.0, 0.0,
                                      0.0, 0.5, 0.0, 0.0,
                                      0.0, 0.0, 0.5, 0.0,
                                      0.5, 0.5, 0.5, 1.0);

      return function ()
      {
         return biasMatrix;
      };
   })(),
   push (renderObject, groupNode)
   {
      const
         path   = `${renderObject .getPath ()},${this .getId ()}`,
         global = this ._global .getValue ();

      let lightContainer;

      if (!renderObject .isIndependent ())
      {
         lightContainer = renderObject .getLightContainer (path);

         if (lightContainer)
            lightContainer .modelViewMatrix .push (renderObject .getModelViewMatrix () .get ());
         else if (global)
            return; // Independent global lights in RenderedTexture scene are not supported.
      }

      lightContainer ??= renderObject .getLights () .get (path);

      if (!lightContainer)
      {
         lightContainer = this .getLights () .pop ();

         if (global)
         {
            lightContainer .set (this,
                                 renderObject .getLayer () .getGroups (),
                                 renderObject .getModelViewMatrix () .get ());
         }
         else
         {
            lightContainer .set (this,
                                 groupNode,
                                 renderObject .getModelViewMatrix () .get ());
         }
      }

      renderObject .getLights () .set (path, lightContainer);

      if (global)
      {
         renderObject .getGlobalLights () .push (lightContainer);
         renderObject .pushGlobalShadows (!! this .getShadowIntensity ());
         renderObject .getGlobalLightsKeys () .push (this .getLightKey ());
      }
      else
      {
         renderObject .getLocalObjects () .push (lightContainer);
         renderObject .pushLocalShadows (!! this .getShadowIntensity ());
         renderObject .getLocalObjectsKeys () .push (this .getLightKey ());
      }
   },
   pop (renderObject)
   {
      if (this ._global .getValue ())
         return;

      const lightContainer = renderObject .getLocalObjects () .pop ();

      this .getBrowser () .getLocalObjects () .add (lightContainer);

      renderObject .popLocalShadows ();
      renderObject .getLocalObjectsKeys () .pop ();
   },
});

Object .defineProperties (X3DLightNode, Core_X3DNode .getStaticProperties ("X3DLightNode", "Lighting", 1));

const X3DLightNode_default_ = X3DLightNode;
;

/* harmony default export */ const Lighting_X3DLightNode = (Namespace/* default */.A .add ("X3DLightNode", X3DLightNode_default_));
;// ./src/x_ite/Components/Lighting/DirectionalLight.js

















const DirectionalLights = Utility_ObjectCache (DirectionalLightContainer);

function DirectionalLightContainer ()
{
   this .direction                     = new Numbers_Vector3 ();
   this .shadowBuffer                  = null;
   this .bbox                          = new Geometry_Box3 ();
   this .viewVolume                    = new Geometry_ViewVolume ();
   this .viewport                      = new Numbers_Vector4 ();
   this .projectionMatrix              = new Numbers_Matrix4 ();
   this .modelViewMatrix               = new Utility_MatrixStack (Numbers_Matrix4);
   this .modelMatrix                   = new Numbers_Matrix4 ();
   this .invLightSpaceMatrix           = new Numbers_Matrix4 ();
   this .invLightSpaceProjectionMatrix = new Numbers_Matrix4 ();
   this .shadowMatrix                  = new Numbers_Matrix4 ();
   this .shadowMatrixArray             = new Float32Array (16);
   this .rotation                      = new Numbers_Rotation4 ();
   this .textureUnit                   = undefined;
}

Object .assign (DirectionalLightContainer .prototype,
{
   set (lightNode, groupNode, modelViewMatrix)
   {
      const shadowMapSize = lightNode .getShadowMapSize ();

      this .browser           = lightNode .getBrowser ();
      this .lightNode         = lightNode;
      this .groupNode         = groupNode;
      this .global            = lightNode .getGlobal ();
      this .shadowMapRendered = false;

      this .modelViewMatrix .push (modelViewMatrix);

      // Get shadow buffer from browser.

      if (lightNode .getShadowIntensity () && shadowMapSize)
         this .shadowBuffer = this .browser .popShadowBuffer (shadowMapSize);
   },
   renderShadowMap (renderObject)
   {
      if (!this .shadowBuffer)
         return;

      if (this .shadowMapRendered)
         return;

      this .shadowMapRendered = true;

      const
         lightNode            = this .lightNode,
         cameraSpaceMatrix    = renderObject .getCameraSpaceMatrix () .get (),
         modelMatrix          = this .modelMatrix .assign (this .modelViewMatrix .get ()) .multRight (cameraSpaceMatrix),
         invLightSpaceMatrix  = this .invLightSpaceMatrix .assign (this .global ? modelMatrix : Numbers_Matrix4 .IDENTITY);

      invLightSpaceMatrix .rotate (this .rotation .setVectors (Numbers_Vector3 .Z_AXIS, this .direction .assign (lightNode .getDirection ()) .negate ()));
      invLightSpaceMatrix .inverse ();

      const
         groupBBox        = this .groupNode .getSubBBox (this .bbox, true), // Group bbox.
         lightBBox        = groupBBox .multRight (invLightSpaceMatrix),     // Group bbox from the perspective of the light.
         shadowMapSize    = lightNode .getShadowMapSize (),
         viewport         = this .viewport .set (0, 0, shadowMapSize, shadowMapSize),
         projectionMatrix = Geometry_Camera .orthoBox (lightBBox, this .projectionMatrix);

      this .shadowBuffer .bind ();

      renderObject .getViewVolumes      () .push (this .viewVolume .set (projectionMatrix, viewport, viewport));
      renderObject .getProjectionMatrix () .push (projectionMatrix);
      renderObject .getModelViewMatrix  () .push (invLightSpaceMatrix);

      renderObject .render (Rendering_TraverseType .SHADOW, this .groupNode .traverse, this .groupNode);

      renderObject .getModelViewMatrix  () .pop ();
      renderObject .getProjectionMatrix () .pop ();
      renderObject .getViewVolumes      () .pop ();

      if (!this .global)
         invLightSpaceMatrix .multLeft (modelMatrix .inverse ());

      this .invLightSpaceProjectionMatrix .assign (invLightSpaceMatrix) .multRight (projectionMatrix) .multRight (lightNode .getBiasMatrix ());
   },
   setGlobalVariables (renderObject)
   {
      this .modelViewMatrix .get () .multDirMatrix (this .direction .assign (this .lightNode .getDirection ())) .normalize ();

      if (!this .shadowBuffer)
         return;

      this .shadowMatrix
         .assign (renderObject .getView () ?.inverse ?? Numbers_Matrix4 .IDENTITY)
         .multRight (renderObject .getCameraSpaceMatrixArray ())
         .multRight (this .invLightSpaceProjectionMatrix);

      this .shadowMatrixArray .set (this .shadowMatrix);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         i        = shaderObject .numLights ++,
         uniforms = shaderObject .x3d_Light [i];

      if (!uniforms)
         return;

      if (this .shadowBuffer)
      {
         const textureUnit = this .global
            ? this .textureUnit ??= this .browser .popGlobalTextureUnit ()
            : this .browser .popTextureUnit ();

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (gl .TEXTURE_2D, this .shadowBuffer .getDepthTexture ());
         gl .uniform1i (uniforms .shadowMap, textureUnit);
      }

      if (shaderObject .hasLight (i, this))
         return;

      const { lightNode, direction} = this;

      gl .uniform1i (uniforms .type,             1);
      gl .uniform3f (uniforms .color,            ... lightNode .getColor ());
      gl .uniform1f (uniforms .intensity,        lightNode .getIntensity ());
      gl .uniform1f (uniforms .ambientIntensity, lightNode .getAmbientIntensity ());
      gl .uniform3f (uniforms .direction,        ... direction);
      gl .uniform1f (uniforms .radius,           -1);

      if (this .shadowBuffer)
      {
         gl .uniform3f        (uniforms .shadowColor,         ... lightNode .getShadowColor ());
         gl .uniform1f        (uniforms .shadowIntensity,     lightNode .getShadowIntensity ());
         gl .uniform1f        (uniforms .shadowBias,          lightNode .getShadowBias ());
         gl .uniformMatrix4fv (uniforms .shadowMatrix, false, this .shadowMatrixArray);
         gl .uniform1i        (uniforms .shadowMapSize,       lightNode .getShadowMapSize ());
      }
      else
      {
         gl .uniform1f (uniforms .shadowIntensity, 0);
      }
   },
   dispose ()
   {
      const { shadowBuffer } = this;

      if (shadowBuffer)
      {
         const { browser, global } = this;

         browser .pushShadowBuffer (shadowBuffer);

         this .shadowBuffer = null;

         if (global)
            this .textureUnit = undefined;
      }

      this .modelViewMatrix .clear ();

      // Return container

      DirectionalLights .push (this);
   },
});

function DirectionalLight (executionContext)
{
   Lighting_X3DLightNode .call (this, executionContext);

   this .addType (Base_X3DConstants .DirectionalLight);

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.0)
      this ._global = true;
}

Object .assign (Object .setPrototypeOf (DirectionalLight .prototype, Lighting_X3DLightNode .prototype),
{
   getLights ()
   {
      return DirectionalLights;
   },
});

Object .defineProperties (DirectionalLight,
{
   ... Core_X3DNode .getStaticProperties ("DirectionalLight", "Lighting", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "global",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "on",               new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",            new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "ambientIntensity", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "direction",        new x_ite_Fields .SFVec3f (0, 0, -1)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadows",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowColor",     new x_ite_Fields .SFColor ()),        // skip test, Color of shadows
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowIntensity", new x_ite_Fields .SFFloat (1)),       // Intensity of shadow color in the range (0, 1)
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowBias",      new x_ite_Fields .SFFloat (0.005)),   // skip test, Bias of the shadow
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "shadowMapSize",   new x_ite_Fields .SFInt32 (1024)),    // skip test, Size of the shadow map in pixels in the range (0, inf).
      ]),
      enumerable: true,
   },
});

const DirectionalLight_default_ = DirectionalLight;
;

/* harmony default export */ const Lighting_DirectionalLight = (Namespace/* default */.A .add ("DirectionalLight", DirectionalLight_default_));
;// ./src/x_ite/Browser/Navigation/X3DNavigationContext.js











const
   _activeCollisions   = Symbol (),
   _viewerNode         = Symbol (),
   _headlightContainer = Symbol ();

function X3DNavigationContext ()
{
   this .addChildObjects (Base_X3DConstants .outputOnly, "availableViewers", new x_ite_Fields .MFString (),
                          Base_X3DConstants .outputOnly, "viewer",           new x_ite_Fields .SFString ("EXAMINE"));

   this [_activeCollisions] = new Set ();
   this [_viewerNode]       = new Navigation_NoneViewer (this .getPrivateScene ());
}

Object .assign (X3DNavigationContext .prototype,
{
   initialize ()
   {
      this ._viewer .addInterest ("set_viewer__", this);

      this .shutdown ()    .addInterest ("remove_world__", this);
      this .initialized () .addInterest ("set_world__",    this);

      this [_headlightContainer] = this .createHeadlight ();
      this [_viewerNode] .setup ();
   },
   createHeadlight ()
   {
      const headlight = new Lighting_DirectionalLight (this .getPrivateScene ());

      headlight .setup ();

      const headlightContainer = headlight .getLights () .pop ();

      headlightContainer .set (headlight, null, Numbers_Matrix4 .IDENTITY);
      headlightContainer .dispose = Function .prototype;

      return headlightContainer;
   },
   getHeadlight ()
   {
      return this [_headlightContainer];
   },
   getActiveLayer ()
   {
      return this ._activeLayer .getValue ();
   },
   getActiveNavigationInfo ()
   {
      return this ._activeNavigationInfo .getValue ();
   },
   getActiveViewpoint ()
   {
      return this ._activeViewpoint .getValue ();
   },
   getCurrentViewer ()
   {
      return this ._viewer .getValue ();
   },
   getViewer ()
   {
      return this [_viewerNode];
   },
   addCollision (object)
   {
      this [_activeCollisions] .add (object);
   },
   removeCollision (object)
   {
      this [_activeCollisions] .delete (object);
   },
   getCollisionCount ()
   {
      return this [_activeCollisions] .size;
   },
   remove_world__ ()
   {
      this [_viewerNode] ?.disconnect ();

      this .getWorld () ._activeLayer .removeInterest ("set_activeLayer__", this);
   },
   set_world__ ()
   {
      this .getWorld () ._activeLayer .addInterest ("set_activeLayer__", this);

      this .set_activeLayer__ ();
   },
   set_activeLayer__ ()
   {
      // Remove active layer.
      {
         const activeLayer = this ._activeLayer .getValue ();

         if (activeLayer)
         {
            activeLayer .getNavigationInfoStack () .removeInterest ("set_activeNavigationInfo__", this);
            activeLayer .getViewpointStack ()      .removeInterest ("set_activeViewpoint__",      this);
         }
      }

      // Add active layer.
      {
         const activeLayer = this ._activeLayer = this .getWorld () .getActiveLayer ();

         if (activeLayer)
         {
            activeLayer .getNavigationInfoStack () .addInterest ("set_activeNavigationInfo__", this);
            activeLayer .getViewpointStack ()      .addInterest ("set_activeViewpoint__",      this);
         }
      }

      this .set_activeNavigationInfo__ ();
      this .set_activeViewpoint__ ();
   },
   set_activeNavigationInfo__ ()
   {
      this ._activeNavigationInfo .getValue () ?._viewer .removeFieldInterest (this ._viewer);

      const navigationInfo = this ._activeLayer .getValue () ?.getNavigationInfo ();

      this ._activeNavigationInfo = navigationInfo;

      navigationInfo ?._viewer .addFieldInterest (this ._viewer);

      this ._viewer = navigationInfo ?._viewer ?? "NONE";
   },
   set_activeViewpoint__ ()
   {
      this ._activeViewpoint = this ._activeLayer .getValue () ?.getViewpoint ();
   },
   set_viewer__ (viewer)
   {
      const navigationInfo = this ._activeNavigationInfo .getValue ();

      this ._availableViewers = navigationInfo ?._availableViewers ?? [ ];

      // Create viewer node.

      this [_viewerNode] ?.dispose ();

      switch (viewer .getValue ())
      {
         case "EXAMINE":
            this [_viewerNode] = new Navigation_ExamineViewer (this .getPrivateScene (), navigationInfo);
            break;
         case "WALK":
            this [_viewerNode] = new Navigation_WalkViewer (this .getPrivateScene (), navigationInfo);
            break;
         case "FLY":
            this [_viewerNode] = new Navigation_FlyViewer (this .getPrivateScene (), navigationInfo);
            break;
         case "PLANE":
            this [_viewerNode] = new Navigation_PlaneViewer (this .getPrivateScene (), navigationInfo);
            break;
         case "NONE":
            this [_viewerNode] = new Navigation_NoneViewer (this .getPrivateScene (), navigationInfo);
            break;
         case "LOOKAT":
            this [_viewerNode] = new Navigation_LookAtViewer (this .getPrivateScene (), navigationInfo);
            break;
         default:
            this [_viewerNode] = new Navigation_ExamineViewer (this .getPrivateScene (), navigationInfo);
            break;
      }

      this [_viewerNode] .setup ();
   },
   dispose ()
   {
      this [_viewerNode] ?.dispose ();
   },
});

const X3DNavigationContext_default_ = X3DNavigationContext;
;

/* harmony default export */ const Navigation_X3DNavigationContext = (Namespace/* default */.A .add ("X3DNavigationContext", X3DNavigationContext_default_));
;// ./src/x_ite/Browser/Networking/X3DNetworkingContext.js






const
   _baseURL        = Symbol (),
   _loadingDisplay = Symbol (),
   _loadingTotal   = Symbol (),
   X3DNetworkingContext_loadingObjects = Symbol (),
   _browserLoading = Symbol (),
   _set_loadCount  = Symbol (),
   _loadFractions  = Symbol (),
   _defaultScene   = Symbol ();

function getBaseURI (element)
{
   const baseURI = element .prop ("baseURI");

   // Fix for Edge.
   if (baseURI .startsWith ("about:"))
      return document .baseURI;

   return baseURI;
}

function X3DNetworkingContext ()
{
   this .addChildObjects (Base_X3DConstants .outputOnly, "loadCount", new x_ite_Fields .SFInt32 ());

   this [_baseURL]        = getBaseURI (this .getElement ());
   this [_browserLoading] = false;
   this [_loadingDisplay] = 0;
   this [_loadingTotal]   = 0;
   this [X3DNetworkingContext_loadingObjects] = new Set ();
   this [_loadFractions]  = new Map ();
}

Object .assign (X3DNetworkingContext .prototype,
{
   initialize ()
   {
      this ._loadCount .addInterest (_set_loadCount, this);
   },
   getScriptURL ()
   {
      return Networking_URLs .getScriptURL ();
   },
   getProviderURL ()
   {
      return Networking_URLs .getProviderURL ();
   },
   getBaseURL ()
   {
      return this [_baseURL];
   },
   setBaseURL (value)
   {
      const
         base = getBaseURI (this .getElement ()),
         url  = new URL (value, base);

      this [_baseURL] = url .protocol .match (/^(?:data|blob):$/) ? base : url .href;
   },
   getBrowserLoading ()
   {
      return this [_browserLoading];
   },
   setBrowserLoading (value)
   {
      this [_browserLoading] = value;

      if (value)
      {
         if (!this [X3DNetworkingContext_loadingObjects] .has (this))
            this .resetLoadCount ();

         this .getShadow () .find (".x_ite-private-world-info") .remove ();

         if (this .getBrowserOption ("SplashScreen"))
         {
            this .getContextMenu () .hide ();
            this .getCanvas () .hide ();
            this .getSplashScreen () .removeClass (["x_ite-private-fade-out-splash-screen", "x_ite-private-hidden"]);
         }
      }
      else
      {
         this .resetLoadCount ();

         if (this .getBrowserOption ("SplashScreen"))
         {
            this .getCanvas () .show ();

            // Defer until promises are resolved.
            setTimeout (() =>
            {
               if (!this [_browserLoading])
                  this .getSplashScreen () .addClass ("x_ite-private-fade-out-splash-screen");
            });
         }
      }
   },
   getLoadingObjects ()
   {
      return this [X3DNetworkingContext_loadingObjects];
   },
   addLoadingObject (object)
   {
      if (!this [X3DNetworkingContext_loadingObjects] .has (object))
         ++ this [_loadingTotal];

      this [X3DNetworkingContext_loadingObjects] .add (object);

      this ._loadCount = this [X3DNetworkingContext_loadingObjects] .size;
   },
   removeLoadingObject (object)
   {
      this [X3DNetworkingContext_loadingObjects] .delete (object);

      this ._loadCount = this [X3DNetworkingContext_loadingObjects] .size;
   },
   getDisplayLoadCount ()
   {
      return Array .from (this [X3DNetworkingContext_loadingObjects]) .reduce ((v, o) => v + !o .isPrivate ?.(), 0);
   },
   resetLoadCount ()
   {
      this ._loadCount       = 0;
      this [_loadingDisplay] = 0;
      this [_loadingTotal]   = 0;

      this [X3DNetworkingContext_loadingObjects] .clear ();
      this [_loadFractions]  .clear ();

      for (const object of this .getPrivateScene () .getLoadingObjects ())
         this .addLoadingObject (object);
   },
   setLoadingFractions (object, fractions)
   {
      if (!this [_browserLoading])
         return;

      // Let fractions go from 1 to 0.
      this [_loadFractions] .set (object, 1 - fractions);
      this [_set_loadCount] ();
   },
   [_set_loadCount] ()
   {
      const loadingDisplay = this .getDisplayLoadCount ();

      let string;

      if (this ._loadCount .getValue () || this [_browserLoading])
      {
         string = ((loadingDisplay || 1) === 1
            ? gettext ("Loading %1 file")
            : gettext ("Loading %1 files")) .replace ("%1", loadingDisplay || 1);
      }
      else
      {
         string = gettext("Loading done");
      }

      this .updateCursor ();

      if (this [_browserLoading])
      {
         const loadFractions = Math .sumPrecise (this [_loadFractions] .values ());

         // Let the loading fractions 1/2 of the count.
         const fractions = 1 - (this ._loadCount .getValue () + loadFractions)
            / (this [_loadingTotal] + this [_loadFractions] .size);

         this .getSplashScreen () .find (".x_ite-private-spinner-text") .text (string);
         this .getSplashScreen () .find (".x_ite-private-progressbar div")
            .css ("width", `${100 * fractions}%`);
      }
      else
      {
         if (loadingDisplay !== this [_loadingDisplay])
            this .setDescription (string);
      }

      this [_loadingDisplay] = loadingDisplay;
   },
   getDefaultScene ()
   {
      // Inline node's empty scene.

      return this [_defaultScene] ??= (() =>
      {
         const defaultScene = new Execution_X3DScene (this);

         defaultScene .setup ();
         defaultScene .setLive (this .isLive ());

         return defaultScene;
      })();
   },
});

const X3DNetworkingContext_default_ = X3DNetworkingContext;
;

/* harmony default export */ const Networking_X3DNetworkingContext = (Namespace/* default */.A .add ("X3DNetworkingContext", X3DNetworkingContext_default_));
;// ./src/x_ite/Browser/Picking/X3DPickingContext.js



const
   _transformSensorNodes = Symbol (),
   _pickSensorNodes      = Symbol (),
   _pickingHierarchy     = Symbol (),
   _pickable             = Symbol (),
   _pickingTime          = Symbol ();

function X3DPickingContext ()
{
   this [_transformSensorNodes] = new Set ();
   this [_pickSensorNodes]      = [ new Set () ];
   this [_pickingHierarchy]     = [ ];
   this [_pickable]             = [ false ];
   this [_pickingTime]          = new Time_StopWatch ();
}

Object .assign (X3DPickingContext .prototype,
{
   addTransformSensor (transformSensorNode)
   {
      this [_transformSensorNodes] .add (transformSensorNode);
      this .enablePicking ();
   },
   removeTransformSensor (transformSensorNode)
   {
      this [_transformSensorNodes] .delete (transformSensorNode);
      this .enablePicking ();
   },
   addPickSensor (pickSensorNode)
   {
      this [_pickSensorNodes] [0] .add (pickSensorNode);
      this .enablePicking ();
   },
   removePickSensor (pickSensorNode)
   {
      this [_pickSensorNodes] [0] .delete (pickSensorNode);
      this .enablePicking ();
   },
   getPickSensors ()
   {
      return this [_pickSensorNodes];
   },
   getPickingHierarchy ()
   {
      return this [_pickingHierarchy];
   },
   getPickable ()
   {
      return this [_pickable];
   },
   enablePicking ()
   {
      if (this [_transformSensorNodes] .size || this [_pickSensorNodes] [0] .size)
         this ._sensorEvents .addInterest ("picking", this);
      else
         this ._sensorEvents .removeInterest ("picking", this);
   },
   picking ()
   {
      this [_pickingTime] .start ();

      this .getWorld () .traverse (Rendering_TraverseType .PICKING);

      for (const transformSensorNode of this [_transformSensorNodes])
      {
         transformSensorNode .process ();
      }

      for (const pickSensorNode of this [_pickSensorNodes] [0])
      {
         pickSensorNode .process ();
      }

      this [_pickingTime] .stop ();
   },
   getPickingTime ()
   {
      return this [_pickingTime];
   },
});

const X3DPickingContext_default_ = X3DPickingContext;
;

/* harmony default export */ const Picking_X3DPickingContext = (Namespace/* default */.A .add ("X3DPickingContext", X3DPickingContext_default_));
;// ./src/x_ite/Browser/PointingDeviceSensor/PointingDevice.js
/* provided dependency */ var PointingDevice_jquery_mousewheel = __webpack_require__(395);
/* provided dependency */ var PointingDevice_$ = __webpack_require__(44).A;



void (typeof PointingDevice_jquery_mousewheel); // import plugin

const CONTEXT_MENU_TIME = 1200;

function PointingDevice (executionContext)
{
   Base_X3DBaseNode .call (this, executionContext);

   this .over     = false;
   this .grabbing = false;
}

Object .assign (Object .setPrototypeOf (PointingDevice .prototype, Base_X3DBaseNode .prototype),
{
   initialize ()
   {
      const element = this .getBrowser () .getSurface ();

      //element .on ("mousewheel.PointingDevice", this .mousewheel .bind (this));
      element .on ("mousedown.PointingDevice" + this .getId (), this .mousedown  .bind (this));
      element .on ("mouseup.PointingDevice"   + this .getId (), this .mouseup    .bind (this));
      element .on ("dblclick.PointingDevice"  + this .getId (), this .dblclick   .bind (this));
      element .on ("mousemove.PointingDevice" + this .getId (), this .mousemove  .bind (this));
      element .on ("mouseout.PointingDevice"  + this .getId (), this .onmouseout .bind (this));

      element .on ("touchstart.PointingDevice" + this .getId (), this .touchstart .bind (this));
      element .on ("touchend.PointingDevice"   + this .getId (), this .touchend   .bind (this));
   },
   mousewheel (event)
   { },
   mousedown (event)
   {
      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      browser .getElement () .focus ();

      if (browser .getShiftKey () && (browser .getControlKey () || browser .getCommandKey ()))
         return;

      if (event .button === 0)
      {
         const { x, y } = browser .getPointerFromEvent (event);

         element .off ("mousemove.PointingDevice" + this .getId ());

         PointingDevice_$(document)
            .on ("mouseup.PointingDevice"   + this .getId (), this .mouseup   .bind (this))
            .on ("mousemove.PointingDevice" + this .getId (), this .mousemove .bind (this))
            .on ("touchend.PointingDevice"  + this .getId (), this .touchend  .bind (this))
            .on ("touchmove.PointingDevice" + this .getId (), this .touchmove .bind (this));

         if (browser .buttonPressEvent (x, y))
         {
            // Stop event propagation.

            event .preventDefault ();
            event .stopImmediatePropagation (); // Keeps the rest of the handlers from being executed

            this .grabbing = Array .from (browser .getHit () .sensors .keys ())
               .some (node => node .getType () .includes (Base_X3DConstants .X3DDragSensorNode));

            browser .setCursor ("POINTER");

            this .onverifymotion (x, y);
         }
      }
   },
   mouseup (event)
   {
      if (event .button !== 0)
         return;

      // Handle button release.

      const
         browser = this .getBrowser (),
         element = browser .getSurface ();

      const { x, y } = browser .getPointerFromEvent (event);

      PointingDevice_$(document) .off (".PointingDevice" + this .getId ());
      element .on ("mousemove.PointingDevice" + this .getId (), this .mousemove .bind (this));

      this .grabbing = false;

      browser .buttonReleaseEvent ();
      browser .setCursor (this .over ? "POINTER" : "DEFAULT");
      this .onverifymotion (x, y);

      if (!this .over)
         return;

      // Stop event propagation.

      event .preventDefault ();
   },
   dblclick (event)
   {
      const browser = this .getBrowser ();

      if (browser .getShiftKey () && (browser .getControlKey () || browser .getCommandKey ()))
         return;

      if (!this .over)
         return;

      // Stop event propagation.

      event .preventDefault ();
      event .stopImmediatePropagation ();
   },
   mousemove (event)
   {
      // Motion.

      const browser = this .getBrowser ();

      const { x, y } = browser .getPointerFromEvent (event);

      this .onmotion (x, y, true);

      if (!this .over)
         return;

      // Stop event propagation.

      event .preventDefault ();
   },
   touchstart (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // button 0.

            event .button = 0;
            event .pageX  = touches [0] .pageX;
            event .pageY  = touches [0] .pageY;

            this .mousedown (event);

            // Show context menu on long tab.

            const hit = this .getBrowser () .getHit ();

            if (hit .id === 0 || hit .sensors .size === 0)
            {
               this .touchX       = event .pageX;
               this .touchY       = event .pageY;
               this .touchTimeout = setTimeout (() => this .showContextMenu (event), CONTEXT_MENU_TIME);
            }

            break;
         }
         case 2:
         {
            this .touchend (event);
            break;
         }
      }
   },
   touchend (event)
   {
      event .button = 0;

      this .mouseup (event);

      clearTimeout (this .touchTimeout);
   },
   touchmove (event)
   {
      const touches = event .originalEvent .touches;

      switch (touches .length)
      {
         case 1:
         {
            // button 0.

            event .button = 0;
            event .pageX  = touches [0] .pageX;
            event .pageY  = touches [0] .pageY;

            this .mousemove (event);

            if (Math .hypot (this .touchX - event .pageX, this .touchY - event .pageY) > 7)
               clearTimeout (this .touchTimeout);

            break;
         }
      }
   },
   onmotion (x, y, move = false)
   {
      const browser = this .getBrowser ();

      this .over = browser .motionNotifyEvent (x, y);

      if (browser .getViewer () .isActive ())
         return;

      if (this .over)
         browser .setCursor (this .grabbing && move ? "GRABBING" : "POINTER");
      else
         browser .setCursor (this .grabbing && move ? "GRABBING" : "DEFAULT");
   },
   onmouseout (event)
   {
      this .getBrowser () .leaveNotifyEvent ();
   },
   async onverifymotion (x, y)
   {
      // Verify isOver state. This is necessary if an Switch changes on buttonReleaseEvent
      // and the new child has a sensor node inside. This sensor node must be updated to
      // reflect the correct isOver state.

      await this .getBrowser () .nextFrame ();

      this .onmotion (x, y);
   },
   showContextMenu (event)
   {
      this .getBrowser () .getContextMenu () .show (event);
   },
});

Object .defineProperties (PointingDevice,
{
   typeName:
   {
      value: "PointingDevice",
      enumerable: true,
   },
});

const PointingDevice_default_ = PointingDevice;
;

/* harmony default export */ const PointingDeviceSensor_PointingDevice = (Namespace/* default */.A .add ("PointingDevice", PointingDevice_default_));
;// ./src/x_ite/Rendering/PointingBuffer.js
function PointingBuffer ({ browser })
{
   const gl = browser .getContext ();

   this .context = gl;
   this .array   = new Float32Array (4);

   // Get current frame buffer.

   const currentFrameBuffer = gl .getParameter (gl .FRAMEBUFFER_BINDING);

   // Create frame buffer.

   this .frameBuffer = gl .createFramebuffer ();

   // Create color buffers.

   this .colorBuffers = [ ];

   for (let i = 0; i < 3; ++ i)
   {
      this .colorBuffers [i] = gl .createRenderbuffer ();

      gl .bindRenderbuffer (gl .RENDERBUFFER, this .colorBuffers [i]);
      gl .renderbufferStorage (gl .RENDERBUFFER, gl .RGBA32F, 1, 1);
      gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);
      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0 + i, gl .RENDERBUFFER, this .colorBuffers [i]);
   }

   gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);

   gl .drawBuffers ([
      gl .COLOR_ATTACHMENT0, // gl_FragData [0]
      gl .COLOR_ATTACHMENT1, // gl_FragData [1]
      gl .COLOR_ATTACHMENT2, // gl_FragData [2]
   ]);

   // Create depth buffer.

   this .depthTexture = gl .createTexture ();

   gl .bindTexture (gl .TEXTURE_2D, this .depthTexture);

   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);
   gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);

   gl .texImage2D (gl .TEXTURE_2D, 0, gl .DEPTH_COMPONENT24, 1, 1, 0, gl .DEPTH_COMPONENT, gl .UNSIGNED_INT, null);
   gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .DEPTH_ATTACHMENT, gl .TEXTURE_2D, this .depthTexture, 0);

   const status = gl .checkFramebufferStatus (gl .FRAMEBUFFER) === gl .FRAMEBUFFER_COMPLETE;

   // Restore current frame buffer.

   gl .bindFramebuffer (gl .FRAMEBUFFER, currentFrameBuffer);

   // Always check that our frame buffer is ok.

   if (!status)
      throw new Error ("Couldn't create frame buffer.");
}

Object .assign (PointingBuffer .prototype,
{
   bind ()
   {
      const gl = this .context;

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);

      // Clear all layers.
      gl .clearColor (0, 0, 0, 0);
      gl .clear (gl .COLOR_BUFFER_BIT);
   },
   getHit (hit)
   {
      const { context: gl, array } = this;

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);

      // Id, point

      gl .readBuffer (gl .COLOR_ATTACHMENT0);
      gl .readPixels (0, 0, 1, 1, gl .RGBA, gl .FLOAT, array);

      hit .id = array [3];
      hit .point .set (array [0], array [1], array [2]);

      // Normal

      gl .readBuffer (gl .COLOR_ATTACHMENT1);
      gl .readPixels (0, 0, 1, 1, gl .RGBA, gl .FLOAT, array);

      hit .normal .set (array [0], array [1], array [2]);

      // TexCoord

      gl .readBuffer (gl .COLOR_ATTACHMENT2);
      gl .readPixels (0, 0, 1, 1, gl .RGBA, gl .FLOAT, array);

      hit .texCoord .set (array [0], array [1], array [2], array [3]);
   },
   dispose ()
   {
      const gl = this .context;

      gl .deleteFramebuffer (this .frameBuffer);

      for (const colorBuffer of this .colorBuffers)
         gl .deleteRenderbuffer (colorBuffer);

      gl .deleteRenderbuffer (this .depthBuffer);
      gl .deleteTexture (this .depthTexture);
   },
});

const PointingBuffer_default_ = PointingBuffer;
;

/* harmony default export */ const Rendering_PointingBuffer = (Namespace/* default */.A .add ("PointingBuffer", PointingBuffer_default_));
;// ./src/x_ite/Browser/PointingDeviceSensor/X3DPointingDeviceSensorContext.js










const
   _pointingDevice            = Symbol (),
   _pointingDeviceSensorNodes = Symbol (),
   _cursorTypes               = Symbol (),
   _cursorType                = Symbol (),
   _pointer                   = Symbol (),
   _hit                       = Symbol (),
   _sensors                   = Symbol (),
   _overSensors               = Symbol (),
   _activeSensors             = Symbol (),
   _pointingLayer             = Symbol (),
   _pointingTime              = Symbol (),
   _pointingBuffer            = Symbol (),
   _pointingShaders           = Symbol (),
   _inputSource               = Symbol (),
   X3DPointingDeviceSensorContext_id                        = Symbol (),
   _pointingContexts          = Symbol (),
   _processEvents             = Symbol .for ("X_ITE.X3DRoutingContext.processEvents");

function X3DPointingDeviceSensorContext ()
{
   this [_pointingDevice]            = new PointingDeviceSensor_PointingDevice (this .getPrivateScene ());
   this [_pointingDeviceSensorNodes] = new Set ();
   this [_pointer]                   = new Numbers_Vector2 ();
   this [_sensors]                   = [ ];
   this [_overSensors]               = new Map ();
   this [_activeSensors]             = new Map ();
   this [_pointingTime]              = new Time_StopWatch ();
   this [_pointingBuffer]            = new Rendering_PointingBuffer ({ browser: this });
   this [_pointingShaders]           = new Map ();
   this [_pointingContexts]          = [ ];

   this [_hit] = {
      id: 0,
      pointer: new Numbers_Vector2 (),
      ray: new Geometry_Line3 (),
      sensors: new Map (),
      viewMatrix: new Numbers_Matrix4 (), // Used by SnapTool.
      modelViewMatrix: new Numbers_Matrix4 (),
      point: new Numbers_Vector3 (),
      normal: new Numbers_Vector3 (), // Must be normalized if used.
      texCoord: new Numbers_Vector4 (),
      layerNode: null,
      pointingLayerNode: null,
      shapeNode: null,
      copy ()
      {
         return {
            id: this .id,
            pointer: this .pointer .copy (),
            ray: this .ray .copy (),
            sensors: new Map (this .sensors),
            viewMatrix: this .viewMatrix .copy (),
            modelViewMatrix: this .modelViewMatrix .copy (),
            point: this .point .copy (),
            normal: this .normal .copy (),
            texCoord: this .texCoord .copy (),
            layerNode: this .layerNode,
            pointingLayerNode: this .pointingLayerNode,
            shapeNode: this .shapeNode,
            copy: this .copy,
         };
      },
   };
}

Object .assign (X3DPointingDeviceSensorContext .prototype,
{
   initialize ()
   {
      this .setCursor ("DEFAULT");

      this [_pointingDevice] .setup ();
   },
   getPointingTime ()
   {
      return this [_pointingTime];
   },
   addPointingDeviceSensor (node)
   {
      this [_pointingDeviceSensorNodes] .add (node);
   },
   removePointingDeviceSensor (node)
   {
      this [_pointingDeviceSensorNodes] .delete (node);
   },
   setCursors (cursorTypes)
   {
      this [_cursorTypes] = cursorTypes;

      this .updateCursor ();
   },
   setCursor (cursorType)
   {
      if (cursorType === this [_cursorType])
         return;

      this [_cursorType] = cursorType;

      this .updateCursor ();
   },
   updateCursor ()
   {
      const
         cursorType = this .getDisplayLoadCount () ? "WAIT" : this [_cursorType],
         css        = this [_cursorTypes] ?.[cursorType] ?? cursorType .toLowerCase ();

      this .getSurface () .css ("cursor", css);
   },
   getPointer ()
   {
      return this [_pointer];
   },
   getPointerFromEvent ({ pageX, pageY })
   {
      const
         offset   = this .getSurface () .offset (),
         rect     = this .getSurface () [0] .getBoundingClientRect (),
         viewport = this .getViewport (),
         x        =      (pageX - offset .left) / rect .width   * viewport [2],
         y        = (1 - (pageY - offset .top)  / rect .height) * viewport [3];

      return new Numbers_Vector2 (x, y);
   },
   isPointerInRectangle (rectangle, pointer = this [_pointer])
   {
      return pointer .x >= rectangle .x &&
             pointer .x <= rectangle .x + rectangle .z &&
             pointer .y >= rectangle .y &&
             pointer .y <= rectangle .y + rectangle .w;
   },
   getPointingInputSource ()
   {
      return this [_inputSource];
   },
   getPointingLayer ()
   {
      return this [_pointingLayer];
   },
   getHit ()
   {
      return this [_hit];
   },
   removeHit (hit)
   {
      hit .id = 0;
      hit .sensors .clear ();

      this .buttonReleaseEvent (hit);
      this .motion (hit);
   },
   addSensor (sensor)
   {
      this [_sensors] .push (sensor);
   },
   addPointingShape (pointingContext)
   {
      const id = ++ this [X3DPointingDeviceSensorContext_id];

      this [_pointingContexts] [id] = pointingContext;

      return id;
   },
   buttonPressEvent (x, y, hit = this [_hit])
   {
      if (!this [_pointingDeviceSensorNodes] .size)
         return false;

      // Must advance time, because event are later processed.
      this .advanceOnlyTime ();

      if (hit === this [_hit])
         this .touch (x, y);

      if (!hit .id)
         return false;

      hit .pointingLayerNode = hit .layerNode;

      for (const [node, sensor] of hit .sensors)
      {
         if (this [_activeSensors] .has (node))
            continue;

         this [_overSensors]   .set (node, sensor);
         this [_activeSensors] .set (node, sensor);

         sensor .set_active__ (true, hit);
      }

      // Immediately process events to be able
      // to do audio and window.open stuff.
      this [_processEvents] ();

      return !! hit .sensors .size;
   },
   buttonReleaseEvent (hit = this [_hit])
   {
      hit .pointingLayerNode = null;

      if (!this [_pointingDeviceSensorNodes] .size)
         return;

      // Must advance time, because event are later processed.
      this .advanceOnlyTime ();

      for (const [node, sensor] of this [_activeSensors])
      {
         if (sensor .hit !== hit)
            continue;

         this [_activeSensors] .delete (node);

         sensor .set_active__ (false, hit);
      }

      // Immediately process events to be able
      // to do audio and window.open stuff.
      this [_processEvents] ();
   },
   motionNotifyEvent (x, y, hit = this [_hit])
   {
      if (!this [_pointingDeviceSensorNodes] .size)
         return false;

      // Must advance time, because events are later processed.
      this .advanceOnlyTime ();

      if (hit === this [_hit])
         this .touch (x, y);

      this .motion (hit);

      // Immediately process events to be able
      // to do audio and window.open stuff.
      this [_processEvents] ();

      return !! hit .sensors .size;
   },
   leaveNotifyEvent ()
   { },
   touch (x, y, hit = this [_hit], inputSource = null)
   {
      if (this .getViewer () .isActive ())
         return false;

      if (Boolean (this .getPose ()) !== Boolean (inputSource))
         return false;

      this [_pointingTime] .start ();

      // Pick.

      this [_inputSource]   = inputSource;
      this [_pointingLayer] = hit .pointingLayerNode;
      this [X3DPointingDeviceSensorContext_id]            = 0;

      this [_pointer] .assign (hit .pointer .set (x, y));
      this [_pointingBuffer] .bind ();

      this .getWorld () .traverse (Rendering_TraverseType .POINTER);

      this [_pointingBuffer] .getHit (hit);

      hit .sensors .clear ();

      if (Number .isInteger (hit .id) && hit .id > 0 && hit .id <= this [X3DPointingDeviceSensorContext_id])
      {
         const
            { renderObject, sensors, modelViewMatrix, shapeNode } = this [_pointingContexts] [hit .id],
            geometryContext = shapeNode .getGeometryContext ();

         hit .ray .assign (renderObject .getHitRay ());

         for (const sensor of sensors)
         {
            sensor .hit = hit;
            hit .sensors .set (sensor .node, sensor);
         }

         hit .layerNode = renderObject;
         hit .shapeNode = shapeNode;

         hit .viewMatrix      .assign (renderObject .getViewpoint () .getViewMatrix ());
         hit .modelViewMatrix .assign (geometryContext .getMatrix ()) .inverse () .multRight (modelViewMatrix);

         // A ParticleSystem has only a geometry context.
         // Hit normal must be normalized if used.

         if (geometryContext .hasNormals)
            hit .modelViewMatrix .submatrix .inverse () .multMatrixVec (hit .normal);
         else
            hit .normal .assign (Numbers_Vector3 .Z_AXIS);
      }
      else
      {
         hit .id = 0;

         if (hit .pointingLayerNode)
            hit .ray .assign (hit .pointingLayerNode .getHitRay ());

         hit .layerNode = null;
         hit .shapeNode = null;

         hit .viewMatrix      .assign (Numbers_Matrix4 .IDENTITY);
         hit .modelViewMatrix .assign (Numbers_Matrix4 .IDENTITY);
      }

      // Dispose unused sensors.

      for (const sensor of this [_sensors])
      {
         if (sensor .hit)
            continue;

         sensor .dispose ();
      }

      this [_sensors] .length = 0;

      // Picking end.

      this [_pointingTime] .stop ();

      return !! hit .id;
   },
   motion (hit = this [_hit])
   {
      // Set isOver to FALSE for appropriate nodes

      for (const [node, sensor] of this [_overSensors])
      {
         if (sensor .hit !== hit)
            continue;

         if (hit .id && hit .sensors .has (node))
            continue;

         this [_overSensors] .delete (node);

         sensor .set_over__ (false, hit);
      }

      // Set isOver to TRUE for appropriate nodes

      for (const [node, sensor] of hit .sensors)
      {
         const overSensor = this [_overSensors] .get (node);

         if (overSensor && overSensor .hit !== hit)
            continue;

         this [_overSensors] .set (node, sensor);

         sensor .set_over__ (true, hit);
      }

      // Forward motion event to active drag sensor nodes

      for (const sensor of this [_activeSensors] .values ())
      {
         if (sensor .hit !== hit)
            continue;

         sensor .set_motion__ (hit);
      }
   },
   getPointingShader (numClipPlanes, shapeNode, hAnimNode)
   {
      const { geometryType, hasNormals } = shapeNode .getGeometryContext ();

      let key = "";

      key += numClipPlanes; // Could be more than 9.
      key += hAnimNode ?.getHAnimKey () ?? "[]";
      key += shapeNode .getShapeKey ();
      key += geometryType;
      key += hasNormals ? 1 : 0;

      if (geometryType >= 2)
      {
         key += "0.0.0";
      }
      else
      {
         const appearanceNode = shapeNode .getAppearance ();

         key += appearanceNode .getStyleProperties (geometryType) ? 1 : 0;
         key += ".";
         key += appearanceNode .getTextureBits () .toString (16); // Textures for point and line.
         key += ".";
         key += appearanceNode .getMaterial () .getTextureBits () .toString (16); // Textures for point and line.
      }

      return this [_pointingShaders] .get (key)
         ?? this .createPointingShader (key, numClipPlanes, shapeNode, hAnimNode);
   },
   createPointingShader (key, numClipPlanes, shapeNode, hAnimNode)
   {
      const
         appearanceNode  = shapeNode .getAppearance (),
         geometryContext = shapeNode .getGeometryContext (),
         options         = [ ];

      options .push ("X3D_POINTING_PASS");

      if (geometryContext .hasNormals)
         options .push ("X3D_NORMALS");

      if (numClipPlanes)
      {
         options .push ("X3D_CLIP_PLANES");
         options .push ("X3D_NUM_CLIP_PLANES " + numClipPlanes);
      }

      switch (shapeNode .getShapeKey ())
      {
         case 1:
         case 2:
            options .push ("X3D_INSTANCING");
            break;
         case 3:
            options .push ("X3D_INSTANCING", "X3D_INSTANCE_NORMAL");
            break;
      }

      options .push (`X3D_GEOMETRY_${geometryContext .geometryType}D`);

      if (appearanceNode .getStyleProperties (geometryContext .geometryType))
         options .push ("X3D_STYLE_PROPERTIES");

      if (+appearanceNode .getMaterial () .getTextureBits ())
         options .push ("X3D_MATERIAL_TEXTURES");

      if (+appearanceNode .getTextureBits ())
         options .push ("X3D_TEXTURE");

      hAnimNode ?.getShaderOptions (options);

      const shaderNode = shapeNode .createPointingShader ?.(options) ?? this .createShader ({
         name: "Pointing",
         vertexShader: "Pointing",
         fragmentShader: "Pointing",
         options,
      });

      this [_pointingShaders] .set (key, shaderNode);

      return shaderNode;
   },
});

const X3DPointingDeviceSensorContext_default_ = X3DPointingDeviceSensorContext;
;

/* harmony default export */ const PointingDeviceSensor_X3DPointingDeviceSensorContext = (Namespace/* default */.A .add ("X3DPointingDeviceSensorContext", X3DPointingDeviceSensorContext_default_));
;// ./src/x_ite/Rendering/MultiSampleFramebuffer.js
function MultiSampleFramebuffer ({ browser, x, y, width, height, samples, oit })
{
   const gl = browser .getContext ();

   this .browser = browser;
   this .context = gl;
   this .x       = x;
   this .y       = y;
   this .width   = width;
   this .height  = height;
   this .samples = samples;
   this .oit     = oit;

   // Create frame buffer.

   this .frameBuffer = gl .createFramebuffer ();

   gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);

   // Create color buffer.

   this .colorBuffer = gl .createRenderbuffer ();

   gl .bindRenderbuffer (gl .RENDERBUFFER, this .colorBuffer);

   if (samples)
      gl .renderbufferStorageMultisample (gl .RENDERBUFFER, samples, gl .RGBA8, width, height);
   else
      gl .renderbufferStorage (gl .RENDERBUFFER, gl .RGBA8, width, height);

   gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .RENDERBUFFER, this .colorBuffer);

   // Create depth buffer.

   this .depthBuffer = gl .createRenderbuffer ();

   gl .bindRenderbuffer (gl .RENDERBUFFER, this .depthBuffer);

   if (samples)
      gl .renderbufferStorageMultisample (gl .RENDERBUFFER, samples, gl .DEPTH_COMPONENT24, width, height);
   else
      gl .renderbufferStorage (gl .RENDERBUFFER, gl .DEPTH_COMPONENT24, width, height);

   gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .DEPTH_ATTACHMENT, gl .RENDERBUFFER, this .depthBuffer);

   // Always check that our frame buffer is ok.

   if (gl .checkFramebufferStatus (gl .FRAMEBUFFER) !== gl .FRAMEBUFFER_COMPLETE)
      throw new Error ("Couldn't create frame buffer.");

   if (x || y)
   {
      // Create frame buffer.

      this .auxBuffer = gl .createFramebuffer ();

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .auxBuffer);

      // Create color buffer.

      this .auxColorBuffer = gl .createRenderbuffer ();

      gl .bindRenderbuffer (gl .RENDERBUFFER, this .auxColorBuffer);
      gl .renderbufferStorage (gl .RENDERBUFFER, gl .RGBA8, width, height);
      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .RENDERBUFFER, this .auxColorBuffer);

      // Always check that our frame buffer is ok.

      if (gl .checkFramebufferStatus (gl .FRAMEBUFFER) !== gl .FRAMEBUFFER_COMPLETE)
         throw new Error ("Couldn't create frame buffer.");
   }

   if (!oit)
      return;

   // Must use 16bit color buffer textures instead of 32bit, because there is an issue with iPhone.

   // Create oit frame buffer.

   this .oitFramebuffer = gl .createFramebuffer ();

   gl .bindFramebuffer (gl .FRAMEBUFFER, this .oitFramebuffer);

   // Set draw buffers.

   gl .drawBuffers ([
      gl .COLOR_ATTACHMENT0, // gl_FragData [0]
      gl .COLOR_ATTACHMENT1, // gl_FragData [1]
   ]);

   if (samples)
   {
      // Create accum and revealage buffer.

      this .accumRevealageBuffer = gl .createRenderbuffer ();

      gl .bindRenderbuffer (gl .RENDERBUFFER, this .accumRevealageBuffer);
      gl .renderbufferStorageMultisample (gl .RENDERBUFFER, samples, gl .RGBA16F, width, height);
      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .RENDERBUFFER, this .accumRevealageBuffer);

      // Create alpha buffer.

      this .alphaBuffer = gl .createRenderbuffer ();

      gl .bindRenderbuffer (gl .RENDERBUFFER, this .alphaBuffer);
      gl .renderbufferStorageMultisample (gl .RENDERBUFFER, samples, gl .RGBA16F, width, height);
      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT1, gl .RENDERBUFFER, this .alphaBuffer);

      // Add depth buffer.

      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .DEPTH_ATTACHMENT, gl .RENDERBUFFER, this .depthBuffer);

      // Create accum texture buffer.

      this .accumRevealageTextureBuffer = gl .createFramebuffer ();

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .accumRevealageTextureBuffer);

      // Create accum texture.

      this .accumRevealageTexture = gl .createTexture ();

      gl .bindTexture (gl .TEXTURE_2D, this .accumRevealageTexture);

      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);

      gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA16F, width, height, 0, gl .RGBA, gl .FLOAT, null);
      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .TEXTURE_2D, this .accumRevealageTexture, 0);

      // Create alpha texture buffer.

      this .alphaTextureBuffer = gl .createFramebuffer ();

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .alphaTextureBuffer);

      // Create alpha texture.

      this .alphaTexture = gl .createTexture ();

      gl .bindTexture (gl .TEXTURE_2D, this .alphaTexture);

      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);

      gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA16F, width, height, 0, gl .RGBA, gl .FLOAT, null);
      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .TEXTURE_2D, this .alphaTexture, 0);
   }
   else
   {
      // Create accum texture.

      this .accumRevealageTexture = gl .createTexture ();

      gl .bindTexture (gl .TEXTURE_2D, this .accumRevealageTexture);

      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);

      gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA16F, width, height, 0, gl .RGBA, gl .FLOAT, null);
      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT0, gl .TEXTURE_2D, this .accumRevealageTexture, 0);

      // Create alpha texture.

      this .alphaTexture = gl .createTexture ();

      gl .bindTexture (gl .TEXTURE_2D, this .alphaTexture);

      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_S,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_WRAP_T,     gl .CLAMP_TO_EDGE);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MIN_FILTER, gl .NEAREST);
      gl .texParameteri (gl .TEXTURE_2D, gl .TEXTURE_MAG_FILTER, gl .NEAREST);

      gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA16F, width, height, 0, gl .RGBA, gl .FLOAT, null);
      gl .framebufferTexture2D (gl .FRAMEBUFFER, gl .COLOR_ATTACHMENT1, gl .TEXTURE_2D, this .alphaTexture, 0);

      // Add depth buffer.

      gl .framebufferRenderbuffer (gl .FRAMEBUFFER, gl .DEPTH_ATTACHMENT, gl .RENDERBUFFER, this .depthBuffer);
   }

   // Always check that our frame buffer is ok.

   if (gl .checkFramebufferStatus (gl .FRAMEBUFFER) !== gl .FRAMEBUFFER_COMPLETE)
      throw new Error ("Couldn't create frame buffer.");

   // Get compose shader and texture units.

   this .shaderNode = browser .getOITComposeShader ();
   this .program    = this .shaderNode .getProgram ();

   gl .useProgram (this .program);

   const
      accumRevealageTextureUnit = gl .getUniformLocation (this .program, "x3d_AccumRevealageTexture"),
      alphaTextureUnit          = gl .getUniformLocation (this .program, "x3d_AlphaTexture");

   gl .uniform1i (accumRevealageTextureUnit, 0);
   gl .uniform1i (alphaTextureUnit,          1);
}

Object .assign (MultiSampleFramebuffer .prototype,
{
   getX ()
   {
      return this .x;
   },
   getY ()
   {
      return this .y;
   },
   getWidth ()
   {
      return this .width;
   },
   getHeight ()
   {
      return this .height;
   },
   getSamples ()
   {
      return this .samples;
   },
   getOIT ()
   {
      return this .oit;
   },
   bind ()
   {
      const { context: gl, frameBuffer } = this;

      gl .bindFramebuffer (gl .FRAMEBUFFER, frameBuffer);
   },
   clear ()
   {
      const { context: gl, frameBuffer, width, height } = this;

      gl .bindFramebuffer (gl .FRAMEBUFFER, frameBuffer);

      gl .viewport (0, 0, width, height);
      gl .scissor  (0, 0, width, height);
      gl .clearColor (0, 0, 0, 0);
      gl .clear (gl .COLOR_BUFFER_BIT);
   },
   bindTransparency ()
   {
      const { context: gl, oitFramebuffer } = this;

      gl .bindFramebuffer (gl .FRAMEBUFFER, oitFramebuffer);

      gl .clearColor (0, 0, 0, 1);
      gl .clear (gl .COLOR_BUFFER_BIT);
      gl .blendFuncSeparate (gl .ONE, gl .ONE, gl .ZERO, gl .ONE_MINUS_SRC_ALPHA);
   },
   compose ()
   {
      const { context: gl, browser, width, height, samples, program } = this;

      // TODO: Combining lights and lights with shadows, can cause feedback loop of texture.
      // TODO: VolumeRendering shader is not made for OIT.

      // Reset viewport before blit, otherwise only last layer size is used.
      gl .viewport (0, 0, width, height);
      gl .scissor  (0, 0, width, height);

      if (samples)
      {
         gl .bindFramebuffer (gl .READ_FRAMEBUFFER, this .oitFramebuffer);

         gl .readBuffer (gl .COLOR_ATTACHMENT0);
         gl .bindFramebuffer (gl .DRAW_FRAMEBUFFER, this .accumRevealageTextureBuffer);

         gl .blitFramebuffer (0, 0, width, height,
                              0, 0, width, height,
                              gl .COLOR_BUFFER_BIT, gl .LINEAR);

         gl .readBuffer (gl .COLOR_ATTACHMENT1);
         gl .bindFramebuffer (gl .DRAW_FRAMEBUFFER, this .alphaTextureBuffer);

         gl .blitFramebuffer (0, 0, width, height,
                              0, 0, width, height,
                              gl .COLOR_BUFFER_BIT, gl .LINEAR);
      }

      gl .useProgram (program);
      gl .activeTexture (gl .TEXTURE0 + 0);
      gl .bindTexture (gl .TEXTURE_2D, this .accumRevealageTexture);
      gl .activeTexture (gl .TEXTURE0 + 1);
      gl .bindTexture (gl .TEXTURE_2D, this .alphaTexture);

      gl .bindFramebuffer (gl .FRAMEBUFFER, this .frameBuffer);
      gl .disable (gl .DEPTH_TEST);
      gl .enable (gl .BLEND);
      gl .blendFunc (gl .ONE, gl .ONE_MINUS_SRC_ALPHA);
      gl .enable (gl .CULL_FACE);
      gl .frontFace (gl .CCW);
      gl .bindVertexArray (browser .getFullscreenVertexArrayObject ());
      gl .drawArrays (gl .TRIANGLES, 0, 6);
      gl .disable (gl .BLEND);
      gl .enable (gl .DEPTH_TEST);
   },
   blit ()
   {
      const { browser, context: gl, x, y, width, height, samples, frameBuffer, auxBuffer } = this;

      // Reset viewport before blit, otherwise only last layer size is used.
      gl .viewport (0, 0, x + width, y + height);
      gl .scissor  (0, 0, x + width, y + height);

      gl .bindFramebuffer (gl .READ_FRAMEBUFFER, frameBuffer);
      gl .bindFramebuffer (gl .DRAW_FRAMEBUFFER, auxBuffer ?? browser .getDefaultFramebuffer ());

      gl .blitFramebuffer (0, 0, width, height,
                           0, 0, width, height,
                           gl .COLOR_BUFFER_BIT, samples ? gl .LINEAR : gl .NEAREST);

      // // DEBUG start
      // if (DEVELOPMENT)
      // {
      //    const volumeScatterBuffer = browser .getVolumeScatterBuffer ?.();

      //    if (volumeScatterBuffer)
      //    {
      //       gl .bindFramebuffer (gl .READ_FRAMEBUFFER, volumeScatterBuffer .frameBuffer);
      //       gl .readBuffer (gl .COLOR_ATTACHMENT0);
      //       gl .bindFramebuffer (gl .DRAW_FRAMEBUFFER, browser .getDefaultFramebuffer ());

      //       gl .blitFramebuffer (0, 0, width, height,
      //                            0, 0, width / 4, height / 4,
      //                            gl .COLOR_BUFFER_BIT, gl .NEAREST);
      //    }
      // }
      // // DEBUG end

      if (!auxBuffer)
         return;

      gl .bindFramebuffer (gl .READ_FRAMEBUFFER, auxBuffer);
      gl .bindFramebuffer (gl .DRAW_FRAMEBUFFER, browser .getDefaultFramebuffer ());

      gl .blitFramebuffer (0, 0, width, height,
                           x, y, x + width, y + height,
                           gl .COLOR_BUFFER_BIT, gl .NEAREST);
   },
   dispose ()
   {
      const gl = this .context;

      gl .deleteFramebuffer (this .frameBuffer);
      gl .deleteRenderbuffer (this .colorBuffer);
      gl .deleteRenderbuffer (this .depthBuffer);

      gl .deleteFramebuffer (this .auxFramebuffer);
      gl .deleteRenderbuffer (this .auxColorBuffer);

      gl .deleteFramebuffer (this .oitFramebuffer);
      gl .deleteFramebuffer (this .accumRevealageTextureBuffer);
      gl .deleteFramebuffer (this .alphaTextureBuffer);
      gl .deleteRenderbuffer (this .accumRevealageBuffer);
      gl .deleteRenderbuffer (this .alphaBuffer);
      gl .deleteTexture (this .accumRevealageTexture);
      gl .deleteTexture (this .alphaTexture);
   },
});

const MultiSampleFramebuffer_default_ = MultiSampleFramebuffer;
;

/* harmony default export */ const Rendering_MultiSampleFramebuffer = (Namespace/* default */.A .add ("MultiSampleFramebuffer", MultiSampleFramebuffer_default_));
;// ./src/standard/Utility/Lock.js
class Lock
{
   static #promises = new Map ();

   static acquire (key, callback)
   {
      if (!this .#promises .has (key))
         this .#promises .set (key, [ ]);

      const
         promises = this .#promises .get (key),
         current  = promises .slice ();

      const p = new Promise (async (resolve, reject) =>
      {
         try
         {
            await Promise .allSettled (current);

            resolve (await callback ());
         }
         catch (error)
         {
            reject (error);
         }
      })
      .finally (() =>
      {
         promises .splice (promises .indexOf (p), 1);

         if (!promises .length)
            this .#promises .delete (key);
      });

      promises .push (p);

      return p;
   }
};

const Lock_default_ = Lock;
;

/* harmony default export */ const Utility_Lock = (Namespace/* default */.A .add ("Lock", Lock_default_));
;// ./src/x_ite/Browser/Rendering/X3DRenderingContext.js
/* provided dependency */ var X3DRenderingContext_$ = __webpack_require__(44).A;






const
   _session             = Symbol (),
   _framebuffers        = Symbol (),
   _defaultFramebuffer  = Symbol (),
   _textureBuffers      = Symbol (),
   _resizer             = Symbol (),
   _localObjects        = Symbol (),
   _fullscreenArray     = Symbol (),
   _fullscreenBuffer    = Symbol (),
   _composeShader       = Symbol (),
   _depthShaders        = Symbol (),
   _shapeId             = Symbol (),
   _buttonLock          = Symbol ();

function X3DRenderingContext ()
{
   this .addChildObjects (Base_X3DConstants .outputOnly, "viewport", new x_ite_Fields .SFVec4f (0, 0, 300, 150));

   this [_framebuffers]   = [ ];
   this [_textureBuffers] = [ ];
   this [_depthShaders]   = new Map ();
   this [_localObjects]   = new Set (); // local objects dumpster

   // WebXR support

   this [_session]            = window;
   this [_defaultFramebuffer] = null;
   this [_buttonLock]         = Symbol ();
}

Object .assign (X3DRenderingContext .prototype,
{
   initialize ()
   {
      // Configure context.

      const gl = this .getContext ();

      gl .enable (gl .SCISSOR_TEST);
      gl .enable (gl .DEPTH_TEST);
      gl .depthFunc (gl .LEQUAL);
      gl .clearDepth (1);

      gl .blendFuncSeparate (gl .SRC_ALPHA, gl .ONE_MINUS_SRC_ALPHA, gl .ONE, gl .ONE_MINUS_SRC_ALPHA);
      gl .blendEquationSeparate (gl .FUNC_ADD, gl .FUNC_ADD);

      // Observe resize and parent changes of <canvas> and configure viewport.

      X3DRenderingContext_$(window) .on (`orientationchange.X3DRenderingContext-${this .getInstanceId ()}`, () => this .reshape ());

      this [_resizer] = new ResizeObserver (() =>
      {
         this .reshape ();

         // Unfortunately jest environment doesn't like a traverse here.
         // TODO: figure out why.
         if (typeof jest !== "undefined")
            return;

         this [Symbol .for ("X_ITE.X3DBrowserContext.traverse")] (performance .now ());
      });

      this [_resizer] .observe (this .getSurface () [0]);

      this .reshape ();

      // Observe fullscreen changes of <x3d-canvas>.

      X3DRenderingContext_$(document) .on ([
         "fullscreenchange",
         "webkitfullscreenchange",
         "mozfullscreenchange",
         "MSFullscreenChange",
      ]
      .map (event => `${event}.X3DRenderingContext-${this .getInstanceId ()}`)
      .join (" "), () => this .onfullscreen ());

      // Check for WebXR support.

      navigator .xr ?.addEventListener ("devicechange", () => this .xrUpdateButton ());

      this .xrUpdateButton ();
   },
   getRenderer ()
   {
      const gl = this .getContext ();

      if (!navigator .userAgent .match (/Firefox/))
      {
         const dbgRenderInfo = gl .getExtension ("WEBGL_debug_renderer_info");

         if (dbgRenderInfo)
            return gl .getParameter (dbgRenderInfo .UNMASKED_RENDERER_WEBGL);
      }

      return gl .getParameter (gl .RENDERER);
   },
   getVendor ()
   {
      const gl = this .getContext ();

      if (!navigator .userAgent .match (/Firefox/))
      {
         const dbgRenderInfo = gl .getExtension ("WEBGL_debug_renderer_info");

         if (dbgRenderInfo)
            return gl .getParameter (dbgRenderInfo .UNMASKED_VENDOR_WEBGL);
      }

      return gl .getParameter (gl .VENDOR);
   },
   getWebGLVersion ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .VERSION);
   },
   getMaxSamples ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_SAMPLES);
   },
   getMaxClipPlanes ()
   {
      return (/* inlined export .maxClipPlanes */6);
   },
   getDepthSize ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .DEPTH_BITS);
   },
   getColorDepth ()
   {
      const gl = this .getContext ();

      return (gl .getParameter (gl .RED_BITS) +
              gl .getParameter (gl .BLUE_BITS) +
              gl .getParameter (gl .GREEN_BITS) +
              gl .getParameter (gl .ALPHA_BITS));
   },
   getViewport ()
   {
      return this ._viewport;
   },
   getLocalObjects ()
   {
      return this [_localObjects];
   },
   getFramebuffers ()
   {
      return this [_framebuffers];
   },
   getDefaultFramebuffer ()
   {
      return this [_defaultFramebuffer];
   },
   setDefaultFramebuffer (defaultFramebuffer)
   {
      this [_defaultFramebuffer] = defaultFramebuffer;

      for (const frameBuffer of this [_framebuffers])
         frameBuffer .dispose ();

      this [_framebuffers] = [ ];

      this .reshape ();
   },
   addTextureBuffer (key)
   {
      this [_textureBuffers] .push (key);
   },
   getFullscreenVertexArrayObject ()
   {
      // Quad for fullscreen rendering.

      return this [_fullscreenArray] ??= (() =>
      {
         const
            gl               = this .getContext (),
            fullscreenArray  = gl .createVertexArray (),
            fullscreenBuffer = gl .createBuffer ();

         this [_fullscreenBuffer] = fullscreenBuffer;

         gl .bindVertexArray (fullscreenArray);
         gl .bindBuffer (gl .ARRAY_BUFFER, fullscreenBuffer);
         gl .bufferData (gl .ARRAY_BUFFER, new Float32Array ([-1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1]), gl .STATIC_DRAW);
         gl .enableVertexAttribArray (0);
         gl .vertexAttribPointer (0, 2, gl .FLOAT, false, 0, 0);

         return fullscreenArray;
      })();
   },
   getOITComposeShader ()
   {
      return this [_composeShader] ??= this .createShader ({
         name: "OITCompose",
         vertexShader: "FullScreen",
         fragmentShader: "OITCompose",
      });
   },
   getDepthShader (normal, numClipPlanes, shapeNode, hAnimNode)
   {
      const geometryContext = shapeNode .getGeometryContext ();

      let key = "";

      key += normal ? 1 : 0;
      key += numClipPlanes; // Could be more than 9.
      key += hAnimNode ?.getHAnimKey () ?? "[]";
      key += shapeNode .getShapeKey ();
      key += geometryContext .geometryType;

      if (geometryContext .geometryType >= 2)
      {
         key += "0.0.0";
      }
      else
      {
         const appearanceNode  = shapeNode .getAppearance ();

         key += appearanceNode .getStyleProperties (geometryContext .geometryType) ? 1 : 0;
         key += ".";
         key += appearanceNode .getTextureBits () .toString (16); // Textures for point and line.
         key += ".";
         key += appearanceNode .getMaterial () .getTextureBits () .toString (16); // Textures for point and line.
      }

      return this [_depthShaders] .get (key)
         ?? this .createDepthShader (key, normal, numClipPlanes, shapeNode, hAnimNode);
   },
   createDepthShader (key, normal, numClipPlanes, shapeNode, hAnimNode)
   {
      const
         appearanceNode  = shapeNode .getAppearance (),
         geometryContext = shapeNode .getGeometryContext (),
         options         = [ ];

      options .push ("X3D_DEPTH_PASS");

      if (normal)
      {
         options .push ("X3D_NORMAL_BUFFER");

         if (geometryContext .hasNormals)
            options .push ("X3D_NORMALS");
      }

      if (numClipPlanes)
      {
         options .push ("X3D_CLIP_PLANES");
         options .push ("X3D_NUM_CLIP_PLANES " + numClipPlanes);
      }

      switch (shapeNode .getShapeKey ())
      {
         case 1:
         case 2:
            options .push ("X3D_INSTANCING");
            break;
         case 3:
            options .push ("X3D_INSTANCING", "X3D_INSTANCE_NORMAL");
            break;
      }

      options .push (`X3D_GEOMETRY_${geometryContext .geometryType}D`);

      if (appearanceNode .getStyleProperties (geometryContext .geometryType))
         options .push ("X3D_STYLE_PROPERTIES");

      hAnimNode ?.getShaderOptions (options);

      const shaderNode = shapeNode .createDepthShader ?.(options) ?? this .createShader ({
         name: "Depth",
         vertexShader: "Depth",
         fragmentShader: "Depth",
         options,
      });

      this [_depthShaders] .set (key, shaderNode);

      return shaderNode;
   },
   resize (width, height)
   {
      return new Promise (resolve =>
      {
         const
            [vWidth, vHeight] = this .limitFramebufferSize (width, height),
            key               = Symbol ();

         const test = () =>
         {
            if (this ._viewport [2] !== vWidth)
               return;

            if (this ._viewport [3] !== vHeight)
               return;

            this ._viewport .removeFieldCallback (key);

            resolve ();
         }

         this .getElement () .css ({ "width": `${width}px`, "height": `${height}px` });

         this ._viewport .addFieldCallback (key, test);

         test ();
      });
   },
   reshape ()
   {
      if (this .getSession () !== window)
         return;

      const
         canvas = this .getCanvas (),
         parent = canvas .parent (),
         [width, height, contentScale] = this .limitFramebufferSize (parent .width (), parent .height ());

      this .getRenderingProperties () ._ContentScale = parent .width () ? width / parent .width () : contentScale;

      // this .println (this .getRenderingProperty ("ContentScale"), devicePixelRatio)

      canvas
         .prop ("width",  width)
         .prop ("height", height);

      this .reshapeFramebuffer (0, 0, 0, width, height);

      this .addBrowserEvent ();
   },
   limitFramebufferSize (width, height)
   {
      const
         gl              = this .getContext (),
         maxViewportDims = gl .getParameter (gl .MAX_VIEWPORT_DIMS);

      let contentScale = this .getBrowserOption ("ContentScale");

      if (contentScale === -1)
         contentScale = devicePixelRatio;

      width  *= contentScale,
      height *= contentScale;

      if (width > maxViewportDims [0] || height > maxViewportDims [1])
      {
         if (width > height)
         {
            height = maxViewportDims [1] * height / width;
            width  = maxViewportDims [0];
         }
         else
         {
            width  = maxViewportDims [0] * width / height;
            height = maxViewportDims [1];
         }
      }

      width  = Math .max (width|0,  1);
      height = Math .max (height|0, 1);

      return [width, height, contentScale];
   },
   reshapeFramebuffer (i, x, y, width, height)
   {
      const
         samples     = this .getRenderingProperty ("Multisampling"),
         oit         = this .getBrowserOption ("OrderIndependentTransparency"),
         frameBuffer = this [_framebuffers] [i];

      if (frameBuffer &&
          x       === frameBuffer .getX () &&
          y       === frameBuffer .getY () &&
          width   === frameBuffer .getWidth () &&
          height  === frameBuffer .getHeight () &&
          samples === frameBuffer .getSamples () &&
          oit     === frameBuffer .getOIT ())
      {
         return;
      }

      this ._viewport [2] = width;
      this ._viewport [3] = height;

      frameBuffer ?.dispose ();

      this [_framebuffers] [i] = new Rendering_MultiSampleFramebuffer ({ browser: this, x, y, width, height, samples, oit });

      for (const key of this [_textureBuffers])
         this .reshapeTextureBuffer (key, width, height);
   },
   reshapeTextureBuffer (key, width, height)
   {
      const textureBuffer = this [key];

      if (!textureBuffer)
         return;

      if (width === textureBuffer .getWidth () && height === textureBuffer .getHeight ())
         return;

      textureBuffer .dispose ();

      this [key] = undefined;
   },
   resetShapeId ()
   {
      this [_shapeId] = 0;
   },
   getShapeId ()
   {
      return ++ this [_shapeId];
   },
   onfullscreen ()
   {
      const element = this .getElement ();

      if (document .fullscreenElement === element [0])
         element .addClass ("x_ite-fullscreen");
      else
         element .removeClass ("x_ite-fullscreen");
   },
   async xrCheckSupport ()
   {
      if (!("xr" in navigator))
         return false;

      const mode = this .getBrowserOption ("XRSessionMode") .toLowerCase () .replaceAll ("_", "-");

      try
      {
         return await navigator .xr .isSessionSupported (mode);
      }
      catch
      {
         return false;
      }
   },
   xrUpdateButton ()
   {
      return Utility_Lock .acquire (this [_buttonLock], async () =>
      {
         this .getSurface () .find (".x_ite-private-xr-button") .remove ();

         if (!await this .xrCheckSupport ())
            return;

         await this .loadComponents (this .getComponent ("WebXR"), this .getComponent ("Geometry2D"));

         this .xrAddButton ();
      });
   },
   xrFrame ()
   { },
   getSession ()
   {
      return this [_session];
   },
   setSession (session)
   {
      this [_session] = session;
   },
   getPose ()
   {
      return null;
   },
   dispose ()
   {
      this [_session] = window;

      this [_resizer] .disconnect ();

      X3DRenderingContext_$(window)   .off (`.X3DRenderingContext-${this .getInstanceId ()}`);
      X3DRenderingContext_$(document) .off (`.X3DRenderingContext-${this .getInstanceId ()}`);
   },
});

const X3DRenderingContext_default_ = X3DRenderingContext;
;

/* harmony default export */ const Rendering_X3DRenderingContext = (Namespace/* default */.A .add ("X3DRenderingContext", X3DRenderingContext_default_));
;// ./src/x_ite/Browser/Scripting/X3DScriptingContext.js
function X3DScriptingContext () { }

Object .assign (X3DScriptingContext .prototype,
{
   isExternal ()
   {
      return !this .getScriptNode ();
   },
   getScriptNode ()
   {
      return null;
   },
});

const X3DScriptingContext_default_ = X3DScriptingContext;
;

/* harmony default export */ const Scripting_X3DScriptingContext = (Namespace/* default */.A .add ("X3DScriptingContext", X3DScriptingContext_default_));
;// ./src/assets/shaders/webgl2/common/ClipPlanes2.glsl.js
const ClipPlanes2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_CLIP_PLANES)
uniform vec4 x3d_ClipPlane[X3D_NUM_CLIP_PLANES];void clip(){for(int i=0;i<X3D_NUM_CLIP_PLANES;++i){vec4 clipPlane=x3d_ClipPlane[i];if(dot(vertex,clipPlane.xyz)<clipPlane.w)discard;}}
#endif
`
;

/* harmony default export */ const ClipPlanes2_glsl = (Namespace/* default */.A .add ("ClipPlanes2.glsl", ClipPlanes2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Fog2.glsl.js
const Fog2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_FOG)
#if defined(X3D_VERTEX_SHADER)&&defined(X3D_FOG_COORDS)
in float x3d_FogDepth;out float fogDepth;void fog(){fogDepth=x3d_FogDepth;}
#endif
#if defined(X3D_FRAGMENT_SHADER)
uniform x3d_FogParameters x3d_Fog;
#if defined(X3D_FOG_COORDS)
in float fogDepth;
#endif
float getFogInterpolant(){
#if defined(X3D_FOG_COORDS)
return clamp(1.-fogDepth,0.,1.);
#else
float visibilityStart=x3d_Fog.visibilityStart;float visibilityRange=x3d_Fog.visibilityRange-visibilityStart;float dV=max(length(x3d_Fog.matrix*vertex)-visibilityStart,0.);
#if defined(X3D_FOG_LINEAR)
return max(visibilityRange-dV,0.)/visibilityRange;
#elif defined(X3D_FOG_EXPONENTIAL)
return exp(-dV/max(visibilityRange-dV,.001));
#endif
#endif
}vec3 getFogColor(const in vec3 color){return mix(x3d_Fog.color,color,getFogInterpolant());}
#endif
#endif
`
;

/* harmony default export */ const Fog2_glsl = (Namespace/* default */.A .add ("Fog2.glsl", Fog2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Fragment2.glsl.js


const Fragment2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_ALPHA_MODE_MASK)
uniform float x3d_AlphaCutoff;
#endif
#if defined(X3D_COLOR_MATERIAL)
in vec4 color;
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
in vec4 texCoord${i};
#endif
`) .join ("\n")}
#endif
#else
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
vec4 texCoord${i} = vec4 (0.0, 0.0, 0.0, 1.0);
#endif
`) .join ("\n")}
#endif
#endif
#if defined(X3D_NORMALS)
in vec3 normal;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
in vec3 localNormal;
#endif
#if defined(X3D_TANGENTS)
in mat3 TBN;
#endif
#else
vec3 normal=vec3(0.,0.,1.);
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
vec3 localNormal=vec3(0.,0.,1.);
#endif
#endif
in vec3 vertex;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
in vec3 localVertex;
#endif
#if!defined(X3D_ORDER_INDEPENDENT_TRANSPARENCY)
out vec4 x3d_FragColor;
#endif
bool frontFacing;
#include<ToneMapping>
#include<Texture>
#include<Normal>
#include<ClipPlanes>
#include<Point>
#include<Stipple>
#include<Hatch>
#include<Fog>
#include<OIT>
#include<Logarithmic>
vec4 getMaterialColor(const in vec4 fragCoord);void main(){frontFacing=gl_FrontFacing;
#if!defined(X3D_NORMALS)&&(defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D))
generateFlatNormals();
#endif
#if defined(X3D_CLIP_PLANES)
clip();
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
stipple(gl_FragCoord);
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
setPointTexCoords(gl_PointCoord);
#elif defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
setTexCoords();
#endif
vec4 finalColor=getMaterialColor(gl_FragCoord);
#if defined(X3D_ALPHA_MODE_OPAQUE)
finalColor.a=1.;
#elif defined(X3D_ALPHA_MODE_MASK)
if(finalColor.a<x3d_AlphaCutoff)discard;finalColor.a=1.;
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
finalColor=getPointColor(finalColor,gl_PointCoord);
#endif
#if(defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D))&&defined(X3D_STYLE_PROPERTIES)
finalColor=getHatchColor(finalColor,gl_FragCoord);
#endif
#if defined(X3D_FOG)
finalColor.rgb=getFogColor(finalColor.rgb);
#endif
finalColor.rgb=toneMap(finalColor.rgb);
#if defined(X3D_ORDER_INDEPENDENT_TRANSPARENCY)
oit(finalColor);
#else
x3d_FragColor=finalColor;
#endif
#if defined(X3D_LOGARITHMIC_DEPTH_BUFFER)
logarithmic();
#endif
}`
;

/* harmony default export */ const Fragment2_glsl = (Namespace/* default */.A .add ("Fragment2.glsl", Fragment2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Hatch2.glsl.js
const Hatch2_glsl_default_ = () => /* glsl */ `
#if(defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D))&&defined(X3D_STYLE_PROPERTIES)
uniform x3d_FillPropertiesParameters x3d_FillProperties;vec4 getHatchColor(const in vec4 color,const in vec4 fragCoord){vec4 finalColor=x3d_FillProperties.filled?color:vec4(0);
#if defined(X3D_STYLE_PROPERTIES_TEXTURE)
vec2 t=fragCoord.xy/(32.*x3d_FillProperties.scale);vec4 hatch=texture(x3d_FillProperties.texture,vec2(t.x,1.-t.y));hatch.rgb*=x3d_FillProperties.hatchColor;finalColor=mix(finalColor,hatch,hatch.a);
#endif
return finalColor;}
#endif
`
;

/* harmony default export */ const Hatch2_glsl = (Namespace/* default */.A .add ("Hatch2.glsl", Hatch2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Instancing2.glsl.js
const Instancing2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_INSTANCING)
#if defined(X3D_TEX_COORD_RAMP)
uniform sampler2D x3d_TexCoordRamp;in vec4 x3d_Particle;vec4 getInstanceTexCoord(const in vec4 texCoord,const in int vertexId){const int map[6]=int[6](0,1,2,0,2,3);int index0=int(x3d_Particle[3]);return texelFetch(x3d_TexCoordRamp,index0+map[vertexId],0);}
#else
#define getInstanceTexCoord(texCoord,vertexId)(texCoord)
#endif
#if defined(X3D_INSTANCE_NORMAL)
in mat3 x3d_InstanceNormalMatrix;vec3 getInstanceNormal(const in vec3 normal){return x3d_InstanceNormalMatrix*normal;}
#else
#define getInstanceNormal(normal)(normal)
#endif
in mat4 x3d_InstanceMatrix;vec4 getInstanceVertex(const in vec4 vertex){return x3d_InstanceMatrix*vertex;}float getInstancePointSize(const in float pointSize){return pointSize*x3d_InstanceMatrix[0].x;}
#else
#define getInstanceVertex(vertex)(vertex)
#define getInstanceNormal(normal)(normal)
#define getInstanceTexCoord(texCoord,vertexId)(texCoord)
#define getInstancePointSize(pointSize)(pointSize)
#endif
`
;

/* harmony default export */ const Instancing2_glsl = (Namespace/* default */.A .add ("Instancing2.glsl", Instancing2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Lighting2.glsl.js
const Lighting2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_LIGHTING)
float getAttenuation(const in vec3 attenuation,const in float distanceToLight){return 1./max(dot(attenuation,vec3(1.,distanceToLight,distanceToLight*distanceToLight)),1.);}float getSpotFactor(const in vec3 pointToLight,const in vec3 direction,const in float cutOffAngle,const in float beamWidth){float spotAngle=acos(clamp(dot(-pointToLight,direction),-1.,1.));if(spotAngle>=cutOffAngle)return 0.;else if(spotAngle<=beamWidth)return 1.;return(spotAngle-cutOffAngle)/(beamWidth-cutOffAngle);}
#endif
`
;

/* harmony default export */ const Lighting2_glsl = (Namespace/* default */.A .add ("Lighting2.glsl", Lighting2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Line22.glsl.js
const Line22_glsl_default_ = () => /* glsl */ `
struct Line2{vec2 point;vec2 direction;};Line2 line2(const in vec2 point1,const in vec2 point2){return Line2(point1,normalize(point2-point1));}vec2 closest_point(const in Line2 line,const in vec2 point){vec2 r=point-line.point;float d=dot(r,line.direction);return line.direction*d+line.point;}`
;

/* harmony default export */ const Line22_glsl = (Namespace/* default */.A .add ("Line22.glsl", Line22_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Logarithmic2.js
const Logarithmic2_default_ = () => /* glsl */ `
#if defined(X3D_LOGARITHMIC_DEPTH_BUFFER)
#if defined(X3D_VERTEX_SHADER)
out float depth;void logarithmic(const in vec4 position){depth=1.+gl_Position.w;}
#endif
#if defined(X3D_FRAGMENT_SHADER)
uniform float x3d_LogarithmicFarFactor1_2;in float depth;void logarithmic(){gl_FragDepth=log2(depth)*x3d_LogarithmicFarFactor1_2;}
#endif
#endif
`
;

/* harmony default export */ const Logarithmic2 = (Namespace/* default */.A .add ("Logarithmic2", Logarithmic2_default_));
;// ./src/assets/shaders/MaterialTextures.js
const MaterialTextures_default_ = {
   names: [
      "x3d_AmbientTexture",
      "x3d_DiffuseTexture",
      "x3d_SpecularTexture",
      "x3d_EmissiveTexture",
      "x3d_ShininessTexture",
      "x3d_BaseTexture",
      "x3d_MetallicRoughnessTexture",
      "x3d_OcclusionTexture",
      "x3d_NormalTexture",
   ],
   add (name)
   {
      this .names .push (name);
   },
   texture (name, components = "rgba", colorSpace = "")
   {
      const ext = !!name .match (/EXT$/);

      name = name .replace (/EXT$/, "");

      const
         type   = ["", "float", "vec2", "vec3", "vec4"] [components .length],
         define = name .replace (/([a-z])([A-Z])/g, "$1_$2") .toUpperCase (),
          EXT   = ext ?  "EXT" : "",
         _EXT   = ext ? "_EXT" : "";

      return /* glsl */ `
#if defined(${define}${_EXT})
uniform ${name}Parameters${EXT}
${name}${EXT};
${type}
get${name .replace (/^x3d_/g, "")}${EXT} (){vec3 texCoord=getTexCoord(${name}${EXT} .textureTransformMapping,${name}${EXT} .textureCoordinateMapping);
#if defined(${define}${_EXT}_FLIP_Y)
texCoord.y=1.-texCoord.y;
#endif
#if defined(${define}${_EXT}_2D)
vec4 textureColor=texture(${name}${EXT} .texture2D,texCoord.st);
#elif defined(${define}${_EXT}_3D)
vec4 textureColor=texture(${name}${EXT} .texture3D,texCoord);
#elif defined(${define}${_EXT}_CUBE)
vec4 textureColor=texture(${name}${EXT} .textureCube,texCoord);
#endif
${type}
textureColorComponents=textureColor.${components};
#if defined(X3D_COLORSPACE_SRGB)
#if defined(${define}${_EXT}_LINEAR)
#if ${colorSpace === "sRGB" || colorSpace === "linear" ? 1 : 0}
textureColorComponents=linearToSRGB(textureColorComponents);
#endif
#endif
#elif defined(X3D_COLORSPACE_LINEAR)
#if!defined(${define}${_EXT}_LINEAR)
#if ${colorSpace === "sRGB" || colorSpace === "linear" ? 1 : 0}
textureColorComponents=sRGBToLinear(textureColorComponents);
#endif
#endif
#endif
return textureColorComponents;}
#endif
`
   },
   structs ()
   {
      return this .names .map (name =>
      {
         const define = name .replace (/([a-z])([A-Z])/g, "$1_$2") .toUpperCase ();

         return /* glsl */ `
#if defined(${define})
struct ${name .replace (/(EXT)?$/, "Parameters$1")}
{mediump int textureTransformMapping;mediump int textureCoordinateMapping;
#if defined(${define}_2D)
mediump sampler2D texture2D;
#endif
#if defined(${define}_3D)
mediump sampler3D texture3D;
#endif
#if defined(${define}_CUBE)
mediump samplerCube textureCube;
#endif
};
#endif
`
      }) .join ("\n");
   }
};
;

/* harmony default export */ const MaterialTextures = (Namespace/* default */.A .add ("MaterialTextures", MaterialTextures_default_));
;// ./src/assets/shaders/webgl2/common/Material2.glsl.js


const Material2_glsl_default_ = () => /* glsl */ `
#include<Fragment>
uniform x3d_MaterialParameters x3d_Material;
#if defined(X3D_LIGHTING)
${MaterialTextures .texture ("x3d_AmbientTexture", "rgb", "sRGB")}
vec3 getAmbientColor(const in vec3 diffuseColor){vec3 ambientColor=x3d_Material.ambientIntensity*diffuseColor;
#if defined(X3D_AMBIENT_TEXTURE)
ambientColor*=getAmbientTexture();
#endif
return ambientColor;}
#endif
${MaterialTextures .texture ("x3d_DiffuseTexture", "rgba", "sRGB")}
vec4 getDiffuseColor(){float alpha=1.-x3d_Material.transparency;
#if defined(X3D_COLOR_MATERIAL)
vec4 diffuseColor=vec4(color.rgb,color.a*alpha);
#else
vec4 diffuseColor=vec4(x3d_Material.diffuseColor,alpha);
#endif
#if defined(X3D_DIFFUSE_TEXTURE)
diffuseColor*=getDiffuseTexture();
#elif defined(X3D_TEXTURE)
diffuseColor=getTextureColor(diffuseColor,vec4(x3d_Material.specularColor,alpha));
#endif
return diffuseColor;}
#if defined(X3D_LIGHTING)
${MaterialTextures .texture ("x3d_SpecularTexture", "rgb", "sRGB")}
vec3 getSpecularColor(){vec3 specularColor=x3d_Material.specularColor;
#if defined(X3D_SPECULAR_TEXTURE)
specularColor*=getSpecularTexture();
#endif
return specularColor;}
#endif
${MaterialTextures .texture ("x3d_EmissiveTexture", "rgb", "sRGB")}
vec3 getEmissiveColor(){vec3 emissiveColor=x3d_Material.emissiveColor;
#if defined(X3D_EMISSIVE_TEXTURE)
emissiveColor*=getEmissiveTexture();
#endif
return emissiveColor;}
#if defined(X3D_LIGHTING)
${MaterialTextures .texture ("x3d_ShininessTexture", "a")}
float getShininessFactor(){float shininess=x3d_Material.shininess;
#if defined(X3D_SHININESS_TEXTURE)
shininess*=getShininessTexture();
#endif
return shininess;}
#endif
${MaterialTextures .texture ("x3d_OcclusionTexture", "r")}
float getOcclusionFactor(){
#if defined(X3D_OCCLUSION_TEXTURE)
return getOcclusionTexture();
#else
return 1.;
#endif
}
#if defined(X3D_LIGHTING)
#include<Lighting>
#include<Shadow>
uniform x3d_LightSourceParameters x3d_LightSource[X3D_NUM_LIGHTS];vec3 getMaterialColor(const in vec3 vertex,const in vec3 N,const in vec3 ambientColor,const in vec3 diffuseColor,const in vec3 specularColor,const in float shininess){vec3 V=normalize(-vertex);vec3 finalColor=vec3(0);for(int i=0;i<X3D_NUM_LIGHTS;++i){x3d_LightSourceParameters light=x3d_LightSource[i];vec3 vL=light.location-vertex;float dL=length(light.matrix*vL);if(dL<=light.radius||light.radius<0.){bool di=light.type==x3d_DirectionalLight;vec3 d=light.direction;vec3 c=light.attenuation;vec3 L=di?-d:normalize(vL);vec3 H=normalize(L+V);float lightAngle=max(dot(N,L),0.);vec3 diffuseTerm=diffuseColor*lightAngle;float specularFactor=shininess>0.?pow(max(dot(N,H),0.),shininess*128.):1.;vec3 specularTerm=specularColor*specularFactor;float attenuationFactor=di?1.:getAttenuation(c,dL);float spotFactor=light.type==x3d_SpotLight?getSpotFactor(L,d,light.cutOffAngle,light.beamWidth):1.;float attenuationSpotFactor=attenuationFactor*spotFactor;vec3 ambientTerm=light.ambientIntensity*ambientColor;vec3 diffuseSpecularTerm=light.intensity*(diffuseTerm+specularTerm);
#if defined(X3D_SHADOWS)
if(lightAngle>0.)diffuseSpecularTerm=mix(diffuseSpecularTerm,light.shadowColor,getShadowIntensity(i,light));
#endif
finalColor+=attenuationSpotFactor*light.color*(ambientTerm+diffuseSpecularTerm);}}return finalColor;}
#endif
#if defined(X3D_GOURAUD_MATERIAL)
vec4 getMaterialColor(const in vec4 fragCoord){float alpha=1.-x3d_Material.transparency;
#if defined(X3D_COLOR_MATERIAL)
vec3 diffuseColor=color.rgb;alpha*=color.a;
#else
vec3 diffuseColor=x3d_Material.diffuseColor;
#endif
#if defined(X3D_LIGHTING)
vec3 ambientColor=getAmbientColor(diffuseColor);vec3 specularColor=getSpecularColor();float shininess=getShininessFactor();float normalScale=x3d_Material.normalScale;vec4 finalColor=vec4(getMaterialColor(vertex,getNormalVector(normalScale),ambientColor,diffuseColor,specularColor,shininess),alpha);
#else
vec4 finalColor=vec4(vec3(0),alpha);
#endif
#if defined(X3D_OCCLUSION_TEXTURE)
finalColor=mix(finalColor,finalColor*getOcclusionFactor(),x3d_Material.occlusionStrength);
#endif
finalColor.rgb+=getEmissiveColor();
#if defined(X3D_DIFFUSE_TEXTURE)
finalColor*=getDiffuseTexture();
#elif defined(X3D_TEXTURE)
finalColor=getTextureColor(finalColor,vec4(x3d_Material.specularColor,alpha));
#endif
#if defined(X3D_TEXTURE_PROJECTION)
diffuseColor*=getTextureProjectorColor();
#endif
return finalColor;}
#endif
#if defined(X3D_PHONG_MATERIAL)
vec4 getMaterialColor(const in vec4 fragCoord){vec4 diffuseColorAlpha=getDiffuseColor();float alpha=diffuseColorAlpha.a;vec3 diffuseColor=diffuseColorAlpha.rgb;
#if defined(X3D_TEXTURE_PROJECTION)
diffuseColor*=getTextureProjectorColor();
#endif
#if defined(X3D_LIGHTING)
vec3 ambientColor=getAmbientColor(diffuseColor);vec3 specularColor=getSpecularColor();float shininess=getShininessFactor();float normalScale=x3d_Material.normalScale;vec3 finalColor=getMaterialColor(vertex,getNormalVector(normalScale),ambientColor,diffuseColor,specularColor,shininess);
#else
vec3 finalColor=vec3(0);
#endif
#if defined(X3D_OCCLUSION_TEXTURE)
finalColor=mix(finalColor,finalColor*getOcclusionFactor(),x3d_Material.occlusionStrength);
#endif
finalColor+=getEmissiveColor();return vec4(finalColor,alpha);}
#endif
`
;

/* harmony default export */ const Material2_glsl = (Namespace/* default */.A .add ("Material2.glsl", Material2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Normal2.glsl.js
const Normal2_glsl_default_ = () => /* glsl */ `
#if!defined(X3D_NORMALS)&&(defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D))
vec3 generateFlatNormal(const in vec3 vertex){vec3 dFdxPos=dFdx(vertex);vec3 dFdyPos=dFdy(vertex);return normalize(cross(dFdxPos,dFdyPos));}void generateFlatNormals(){normal=generateFlatNormal(vertex);
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
localNormal=generateFlatNormal(localVertex);
#endif
}
#endif
#if defined(X3D_LIGHTING)||defined(X3D_USE_IBL)||defined(X3D_ANISOTROPY_MATERIAL_EXT)||defined(X3D_CLEARCOAT_MATERIAL_EXT)
struct NormalInfo{vec3 ng;vec3 ntex;vec3 n;vec3 t;vec3 b;};
#if defined(X3D_NORMAL_TEXTURE)
uniform x3d_NormalTextureParameters x3d_NormalTexture;
#endif
NormalInfo getNormalInfo(const in float normalScale){
#if defined(X3D_NORMAL_TEXTURE)
vec3 UV=getTexCoord(x3d_NormalTexture.textureTransformMapping,x3d_NormalTexture.textureCoordinateMapping);
#if defined(X3D_NORMAL_TEXTURE_FLIP_Y)
UV.y=1.-UV.y;
#endif
#else
vec3 UV=vertex;
#endif
vec3 n,t,b,ng;
#if defined(X3D_NORMALS)&&defined(X3D_TANGENTS)
t=normalize(TBN[0]);b=normalize(TBN[1]);ng=normalize(TBN[2]);
#else
vec2 uv_dx=dFdx(UV.st);vec2 uv_dy=dFdy(UV.st);if(length(uv_dx)<=1e-2)uv_dx=vec2(1.,0.);if(length(uv_dy)<=1e-2)uv_dy=vec2(0.,1.);vec3 t_=(uv_dy.t*dFdx(vertex)-uv_dx.t*dFdy(vertex))/(uv_dx.s*uv_dy.t-uv_dy.s*uv_dx.t);ng=normalize(normal);t=normalize(t_-ng*dot(ng,t_));b=cross(ng,t);
#endif
#if defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D)
if(frontFacing==false){t=-t;b=-b;ng=-ng;}
#endif
NormalInfo info;info.ng=ng;
#if defined(X3D_NORMAL_TEXTURE)
#if defined(X3D_NORMAL_TEXTURE_2D)
vec3 ntex=texture(x3d_NormalTexture.texture2D,UV.st).rgb;
#elif defined(X3D_NORMAL_TEXTURE_3D)
vec3 ntex=texture(x3d_NormalTexture.texture3D,UV).rgb;
#elif defined(X3D_NORMAL_TEXTURE_CUBE)
vec3 ntex=texture(x3d_NormalTexture.textureCube,UV).rgb;
#endif
ntex=ntex*2.-vec3(1);ntex*=vec3(vec2(normalScale),1.);ntex=normalize(ntex);info.ntex=ntex;info.n=normalize(mat3(t,b,ng)*ntex);
#else
info.n=ng;
#endif
info.t=t;info.b=b;return info;}
#endif
#if defined(X3D_LIGHTING)
vec3 getNormalVector(const in float normalScale){return getNormalInfo(normalScale).n;}
#endif
`
;

/* harmony default export */ const Normal2_glsl = (Namespace/* default */.A .add ("Normal2.glsl", Normal2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/OIT2.js
const OIT2_default_ = () => /* glsl */ `
#if defined(X3D_ORDER_INDEPENDENT_TRANSPARENCY)
#if defined(X3D_FRAGMENT_SHADER)
layout(location=0)out vec4 x3d_FragData0;layout(location=1)out vec4 x3d_FragData1;float weight(const in float z,const in float a){return clamp(pow(min(1.,a*10.)+.01,3.)*1e8*pow(1.-z*.9,3.),1e-2,3e3);}void oit(in vec4 finalColor){float a=finalColor.a;float w=weight(gl_FragCoord.z,a);finalColor.rgb*=a;finalColor*=w;x3d_FragData0=vec4(finalColor.rgb,a);x3d_FragData1=vec4(finalColor.a);}
#endif
#endif
`
;

/* harmony default export */ const OIT2 = (Namespace/* default */.A .add ("OIT2", OIT2_default_));
;// ./src/assets/shaders/webgl2/common/Perlin2.glsl.js
const Perlin2_glsl_default_ = () => /* glsl */ `
float rand(const in vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}float rand(const in vec2 co,const in float l){return rand(vec2(rand(co),l));}float rand(const in vec2 co,const in float l,const in float t){return rand(vec2(rand(co,l),t));}float perlin(const in vec2 p,const in float dim,const in float time){const float M_PI=3.14159265358979323846;vec2 pos=floor(p*dim);vec2 posx=pos+vec2(1.,0.);vec2 posy=pos+vec2(0.,1.);vec2 posxy=pos+vec2(1);float c=rand(pos,dim,time);float cx=rand(posx,dim,time);float cy=rand(posy,dim,time);float cxy=rand(posxy,dim,time);vec2 d=fract(p*dim);d=-.5*cos(d*M_PI)+.5;float ccx=mix(c,cx,d.x);float cycxy=mix(cy,cxy,d.x);float center=mix(ccx,cycxy,d.y);return center*2.-1.;}vec3 perlin(const in vec3 p){return vec3(perlin(p.xy,1.,0.),perlin(p.yz,1.,0.),perlin(p.zx,1.,0.));}`
;

/* harmony default export */ const Perlin2_glsl = (Namespace/* default */.A .add ("Perlin2.glsl", Perlin2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Point2.glsl.js


const Point2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
#if!defined(X3D_DEPTH_PASS)&&!defined(X3D_POINTING_PASS)
void setPointTexCoords(const in vec2 pointCoord){vec4 texCoord=vec4(pointCoord.x,1.-pointCoord.y,0.,1.);
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
texCoords [${i}] = texCoord;
#endif
`) .join ("\n")}
}
#endif
#define getPointColor(color,pointCoord)(color)
#else
in float pointSize;
#define setPointTexCoords(pointCoord)
vec4 getPointColor(in vec4 color,const in vec2 pointCoord){if(pointSize>1.)color.a*=clamp(pointSize*(.5-distance(vec2(.5),pointCoord)),0.,1.);else color.a*=pointSize;return color;}
#endif
#endif
`
;

/* harmony default export */ const Point2_glsl = (Namespace/* default */.A .add ("Point2.glsl", Point2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/PointSize2.glsl.js
const PointSize2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
uniform x3d_PointPropertiesParameters x3d_PointProperties;out float pointSize;float getPointSize(const in vec3 vertex){float pointSizeMinValue=x3d_PointProperties.pointSizeMinValue;float pointSizeMaxValue=x3d_PointProperties.pointSizeMaxValue;vec3 attenuation=x3d_PointProperties.attenuation;float dL=length(vertex);float pointSize=0.;pointSize=x3d_PointProperties.pointSizeScaleFactor;pointSize/=dot(attenuation,vec3(1.,dL,dL*dL));pointSize=clamp(pointSize,pointSizeMinValue,pointSizeMaxValue);
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURE)
return pointSize;
#else
return pointSize+1.-step(pointSize,1.);
#endif
}
#endif
`
;

/* harmony default export */ const PointSize2_glsl = (Namespace/* default */.A .add ("PointSize2.glsl", PointSize2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Shadow2.glsl.js
const maxLights = 8;

const Shadow2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_SHADOWS)
uniform sampler2D x3d_ShadowMap[X3D_NUM_LIGHTS];float getShadowDepth(const in int index,const in vec2 shadowCoord){switch(index){
${Array .from ({ length: maxLights }, (_, i) => /* glsl */ `
#if X3D_NUM_LIGHTS > ${i}
case ${i}:
return texture (x3d_ShadowMap [${i}], shadowCoord) .r;
#endif
`) .join ("\n")}
}return 0.;}float texture2DCompare(const in int index,const in vec2 texCoord,const in float compare){float shadowDepth=getShadowDepth(index,texCoord);return(1.-step(1.,shadowDepth))*step(shadowDepth,compare);}float texture2DShadowLerp(const in int index,const in vec2 texelSize,const in float shadowMapSize,const in vec2 texCoord,const in float compare){const vec2 offset=vec2(0.,1.);vec2 centroidTexCoord=floor(texCoord*shadowMapSize+.5)/shadowMapSize;float lb=texture2DCompare(index,centroidTexCoord+texelSize*offset.xx,compare);float lt=texture2DCompare(index,centroidTexCoord+texelSize*offset.xy,compare);float rb=texture2DCompare(index,centroidTexCoord+texelSize*offset.yx,compare);float rt=texture2DCompare(index,centroidTexCoord+texelSize*offset.yy,compare);vec2 f=fract(texCoord*shadowMapSize+.5);float a=mix(lb,lt,f.y);float b=mix(rb,rt,f.y);float c=mix(a,b,f.x);return c;}vec2 cubeToUVCompact(in vec3 v,const float texelSizeY){vec3 absV=abs(v);float scaleToCube=1./max(absV.x,max(absV.y,absV.z));absV*=scaleToCube;v*=scaleToCube*(1.-2.*texelSizeY);vec2 planar=v.xy;float almostATexel=1.5*texelSizeY;float almostOne=1.-almostATexel;if(absV.z>=almostOne){if(v.z>0.)planar.x=4.-v.x;}else if(absV.x>=almostOne){float signX=sign(v.x);planar.x=v.z*signX+2.*signX;}else if(absV.y>=almostOne){float signY=sign(v.y);planar.x=(v.x+.5+signY)*2.;planar.y=v.z*signY-2.;}return vec2(.125,.25)*planar+vec2(.375,.75);}mat4 getPointLightRotations(const in vec3 vector){const mat4 rotations[6]=mat4[6](mat4(0,0,1,0,0,1,0,0,-1,0,0,0,0,0,0,1),mat4(0,0,-1,0,0,1,0,0,1,0,0,0,0,0,0,1),mat4(-1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1),mat4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),mat4(1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1),mat4(1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1));vec3 a=abs(vector.xyz);if(a.x>a.y){if(a.x>a.z)return vector.x>0.?rotations[1]:rotations[0];else return vector.z>0.?rotations[2]:rotations[3];}else{if(a.y>a.z)return vector.y>0.?rotations[5]:rotations[4];else return vector.z>0.?rotations[2]:rotations[3];}return rotations[3];}float getShadowIntensity(const in int index,const in x3d_LightSourceParameters light){if(light.type==x3d_PointLight){const mat4 biasMatrix=mat4(.5,0.,0.,0.,0.,.5,0.,0.,0.,0.,.5,0.,.5,.5,.5,1.);const mat4 projectionMatrix=mat4(1.,0.,0.,0.,0.,.5,0.,0.,0.,0.,-1.000025000312504,-1.,0,0.,-.25000312503906297,0.);vec2 texelSize=vec2(1)/(float(light.shadowMapSize)*vec2(4.,2.));vec4 shadowCoord=light.shadowMatrix*vec4(vertex,1.);vec3 lightToPosition=shadowCoord.xyz;shadowCoord=biasMatrix*(projectionMatrix*(getPointLightRotations(lightToPosition)*shadowCoord));shadowCoord.z-=light.shadowBias;shadowCoord.xyz/=shadowCoord.w;
#if defined(X3D_PCF_FILTERING)||defined(X3D_PCF_SOFT_FILTERING)
vec2 offset=vec2(-1,1)*(texelSize.y*42.);float value=(texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.xyy,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.yyy,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.xyx,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.yyx,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.xxy,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.yxy,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.xxx,texelSize.y),shadowCoord.z)+texture2DCompare(index,cubeToUVCompact(lightToPosition+offset.yxx,texelSize.y),shadowCoord.z))*(1./9.);return light.shadowIntensity*value;
#else
float value=texture2DCompare(index,cubeToUVCompact(lightToPosition,texelSize.y),shadowCoord.z);return light.shadowIntensity*value;
#endif
}else{
#if defined(X3D_PCF_FILTERING)
vec2 texelSize=vec2(1)/vec2(light.shadowMapSize);vec4 shadowCoord=light.shadowMatrix*vec4(vertex,1.);shadowCoord.z-=light.shadowBias;shadowCoord.xyz/=shadowCoord.w;if(any(greaterThan(abs(shadowCoord.xy-.5),vec2(.5))))return 0.;vec2 d0=-texelSize;vec2 d1=texelSize;float value=(texture2DCompare(index,shadowCoord.xy+d0,shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(0.,d0.y),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(d1.x,d0.y),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(d0.x,0.),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy,shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(d1.x,0.),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(d0.x,d1.y),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+vec2(0.,d1.y),shadowCoord.z)+texture2DCompare(index,shadowCoord.xy+d1,shadowCoord.z))*(1./9.);return light.shadowIntensity*value;
#elif defined(X3D_PCF_SOFT_FILTERING)
vec2 texelSize=vec2(1)/vec2(light.shadowMapSize);vec4 shadowCoord=light.shadowMatrix*vec4(vertex,1.);shadowCoord.z-=light.shadowBias;shadowCoord.xyz/=shadowCoord.w;if(any(greaterThan(abs(shadowCoord.xy-.5),vec2(.5))))return 0.;vec2 d0=-texelSize;vec2 d1=texelSize;float value=(texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+d0,shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(0.,d0.y),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(d1.x,d0.y),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(d0.x,0.),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy,shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(d1.x,0.),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(d0.x,d1.y),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+vec2(0.,d1.y),shadowCoord.z)+texture2DShadowLerp(index,texelSize,float(shadowMapSize),shadowCoord.xy+d1,shadowCoord.z))*(1./9.);return light.shadowIntensity*value;
#else
vec4 shadowCoord=light.shadowMatrix*vec4(vertex,1.);shadowCoord.z-=light.shadowBias;shadowCoord.xyz/=shadowCoord.w;if(any(greaterThan(abs(shadowCoord.xy-.5),vec2(.5))))return 0.;float value=texture2DCompare(index,shadowCoord.xy,shadowCoord.z);return light.shadowIntensity*value;
#endif
}return 0.;}
#endif
`
;

/* harmony default export */ const Shadow2_glsl = (Namespace/* default */.A .add ("Shadow2.glsl", Shadow2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Stipple2.glsl.js
const Stipple2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
#include<Line2>
uniform x3d_LinePropertiesParameters x3d_LineProperties;flat in float lengthSoFar;flat in vec2 startPoint;in vec2 midPoint;void stipple(const in vec4 fragCoord){vec2 point=closest_point(line2(startPoint,midPoint),fragCoord.xy);float s=(lengthSoFar+length(point-startPoint))*x3d_LineProperties.lineStippleScale;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
#if X3D_NUM_TEXTURE_COORDINATES>0
texCoord0=vec4(s,0.,0.,1.);
#endif
#if X3D_NUM_TEXTURE_COORDINATES>1
texCoord1=vec4(s,0.,0.,1.);
#endif
#endif
#if defined(X3D_STYLE_PROPERTIES_TEXTURE)
if(x3d_LineProperties.linetype==16)return;int linetype=x3d_LineProperties.linetype;int height=textureSize(x3d_LineProperties.texture,0).y;float t=1.-float(linetype*2+1)/float(height*2);float alpha=texture(x3d_LineProperties.texture,vec2(s,1.-t)).a;if(alpha!=1.)discard;
#endif
}
#endif
`
;

/* harmony default export */ const Stipple2_glsl = (Namespace/* default */.A .add ("Stipple2.glsl", Stipple2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Texture2.glsl.js


const Texture2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
#include<Perlin>
vec4 texCoords[X3D_NUM_TEXTURE_COORDINATES];void setTexCoords(){
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
texCoords [${i}] = texCoord${i};
#endif
`) .join ("\n")}
}uniform mat4 x3d_TextureMatrix[X3D_NUM_TEXTURE_TRANSFORMS];uniform x3d_TextureCoordinateGeneratorParameters x3d_TextureCoordinateGenerator[X3D_NUM_TEXTURE_COORDINATES];vec4 getTexCoord(const in int textureCoordinateMapping){x3d_TextureCoordinateGeneratorParameters textureCoordinateGenerator=x3d_TextureCoordinateGenerator[textureCoordinateMapping];switch(textureCoordinateGenerator.mode){case x3d_Sphere:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec2 N=normalize(normal).xy;
#else
vec2 N=normalize(frontFacing?normal:-normal).xy;
#endif
return vec4(N*.5+.5,0.,1.);}case x3d_CameraSpaceNormal:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec3 N=normalize(normal);
#else
vec3 N=normalize(frontFacing?normal:-normal);
#endif
return vec4(N,1.);}case x3d_CameraSpacePosition:{return vec4(vertex,1.);}case x3d_CameraSpaceReflectionVector:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec3 N=normalize(normal);
#else
vec3 N=normalize(frontFacing?normal:-normal);
#endif
return vec4(reflect(normalize(vertex),-N),1.);}case x3d_SphereLocal:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec2 N=normalize(localNormal).xy;
#else
vec2 N=normalize(frontFacing?localNormal:-localNormal).xy;
#endif
return vec4(N*.5+.5,0.,1.);}case x3d_Coord:{return vec4(localVertex,1.);}case x3d_CoordEye:{return vec4(vertex,1.);}case x3d_Noise:{vec3 scale=vec3(textureCoordinateGenerator.parameter[0],textureCoordinateGenerator.parameter[1],textureCoordinateGenerator.parameter[2]);vec3 translation=vec3(textureCoordinateGenerator.parameter[3],textureCoordinateGenerator.parameter[4],textureCoordinateGenerator.parameter[5]);return vec4(perlin(localVertex*scale+translation),1.);}case x3d_NoiseEye:{vec3 scale=vec3(textureCoordinateGenerator.parameter[0],textureCoordinateGenerator.parameter[1],textureCoordinateGenerator.parameter[2]);vec3 translation=vec3(textureCoordinateGenerator.parameter[3],textureCoordinateGenerator.parameter[4],textureCoordinateGenerator.parameter[5]);return vec4(perlin(vertex*scale+translation),1.);}case x3d_SphereReflect:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec3 N=normalize(normal);
#else
vec3 N=normalize(frontFacing?normal:-normal);
#endif
float eta=textureCoordinateGenerator.parameter[0];return vec4(refract(normalize(vertex),-N,eta),1.);}case x3d_SphereReflectLocal:{
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec3 N=normalize(localNormal);
#else
vec3 N=normalize(frontFacing?localNormal:-localNormal);
#endif
float eta=textureCoordinateGenerator.parameter[0];vec3 eye=vec3(textureCoordinateGenerator.parameter[1],textureCoordinateGenerator.parameter[2],textureCoordinateGenerator.parameter[3]);return vec4(refract(normalize(localVertex-eye),-N,eta),1.);}default:{return texCoords[textureCoordinateMapping];}}}vec3 getTexCoord(const in int textureTransformMapping,const in int textureCoordinateMapping){vec4 texCoord=getTexCoord(textureCoordinateMapping);
#if defined(X3D_GEOMETRY_2D)
if(frontFacing==false)texCoord.x=1.-texCoord.x;
#endif
texCoord=x3d_TextureMatrix[textureTransformMapping]*texCoord;texCoord.stp/=texCoord.q;return texCoord.stp;}
#endif
#if defined(X3D_TEXTURE)
uniform sampler2D x3d_Texture2D[X3D_NUM_TEXTURES];uniform sampler3D x3d_Texture3D[X3D_NUM_TEXTURES];uniform samplerCube x3d_TextureCube[X3D_NUM_TEXTURES];vec4 getTexture(const in int i,const in int textureTransformMapping,const in int textureCoordinateMapping){vec4 textureColor=vec4(1);switch(i){
${Array .from ({ length: (/* inlined export .maxTextures */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURES > ${i}
case ${i}:
{
vec3 texCoord = getTexCoord (textureTransformMapping, textureCoordinateMapping);
#if defined (X3D_TEXTURE_${i}_FLIP_Y)
// Flip Y if needed. Must be done after.
texCoord .y = 1.0 - texCoord .y;
#endif
#if defined (X3D_TEXTURE_${i}_2D)
textureColor = texture (x3d_Texture2D [${i}], texCoord .st);
#elif defined (X3D_TEXTURE_${i}_3D)
textureColor = texture (x3d_Texture3D [${i}], texCoord .stp);
#elif defined (X3D_TEXTURE_${i}_CUBE)
textureColor = texture (x3d_TextureCube [${i}], texCoord .stp);
#endif
#if defined (X3D_COLORSPACE_SRGB)
#if defined (X3D_TEXTURE_${i}_LINEAR)
textureColor = linearToSRGB (textureColor);
#endif
#elif defined (X3D_COLORSPACE_LINEAR)
#if !defined (X3D_TEXTURE_${i}_LINEAR)
textureColor = sRGBToLinear (textureColor);
#endif
#endif
break;
}
#endif
`) .join ("\n")}
}return textureColor;}
#if defined(X3D_MULTI_TEXTURING)
uniform vec4 x3d_MultiTextureColor;uniform x3d_MultiTextureParameters x3d_MultiTexture[X3D_NUM_TEXTURES];
#endif
vec4 getTextureColor(const in vec4 diffuseColor,const in vec4 specularColor){
#if defined(X3D_MULTI_TEXTURING)
vec4 currentColor=diffuseColor;for(int i=0;i<X3D_NUM_TEXTURES;++i){vec4 textureColor=getTexture(i,min(i,X3D_NUM_TEXTURE_TRANSFORMS-1),min(i,X3D_NUM_TEXTURE_COORDINATES-1));x3d_MultiTextureParameters multiTexture=x3d_MultiTexture[i];vec4 arg1=textureColor;vec4 arg2=currentColor;int source=multiTexture.source;switch(source){case x3d_Diffuse:{arg1=diffuseColor;break;}case x3d_Specular:{arg1=specularColor;break;}case x3d_Factor:{arg1=x3d_MultiTextureColor;break;}}int function=multiTexture.function;switch(function){case x3d_Complement:{arg1=1.-arg1;break;}case x3d_AlphaReplicate:{arg1.a=arg2.a;break;}}int mode=multiTexture.mode;int alphaMode=multiTexture.alphaMode;switch(mode){case x3d_Replace:{currentColor.rgb=arg1.rgb;break;}case x3d_Modulate:{currentColor.rgb=arg1.rgb*arg2.rgb;break;}case x3d_Modulate2X:{currentColor.rgb=(arg1.rgb*arg2.rgb)*2.;break;}case x3d_Modulate4X:{currentColor.rgb=(arg1.rgb*arg2.rgb)*4.;break;}case x3d_Add:{currentColor.rgb=arg1.rgb+arg2.rgb;break;}case x3d_AddSigned:{currentColor.rgb=arg1.rgb+arg2.rgb-.5;break;}case x3d_AddSigned2X:{currentColor.rgb=(arg1.rgb+arg2.rgb-.5)*2.;break;}case x3d_AddSmooth:{currentColor.rgb=arg1.rgb+(1.-arg1.rgb)*arg2.rgb;break;}case x3d_Subtract:{currentColor.rgb=arg1.rgb-arg2.rgb;break;}case x3d_BlendDiffuseAlpha:{currentColor.rgb=arg1.rgb*diffuseColor.a+arg2.rgb*(1.-diffuseColor.a);break;}case x3d_BlendTextureAlpha:{currentColor.rgb=arg1.rgb*arg1.a+arg2.rgb*(1.-arg1.a);break;}case x3d_BlendFactorAlpha:{currentColor.rgb=arg1.rgb*x3d_MultiTextureColor.a+arg2.rgb*(1.-x3d_MultiTextureColor.a);break;}case x3d_BlendCurrentAlpha:{currentColor.rgb=arg1.rgb*arg2.a+arg2.rgb*(1.-arg2.a);break;}case x3d_ModulateAlphaAddColor:{currentColor.rgb=arg1.rgb+arg1.a*arg2.rgb;break;}case x3d_ModulateInvAlphaAddColor:{currentColor.rgb=(1.-arg1.a)*arg2.rgb+arg1.rgb;break;}case x3d_ModulateInvColorAddAlpha:{currentColor.rgb=(1.-arg1.rgb)*arg2.rgb+arg1.a;break;}case x3d_DotProduct3:{currentColor.rgb=vec3(dot(arg1.rgb*2.-1.,arg2.rgb*2.-1.));break;}case x3d_SelectArg1:{currentColor.rgb=arg1.rgb;break;}case x3d_SelectArg2:{currentColor.rgb=arg2.rgb;break;}case x3d_Off:{break;}}switch(alphaMode){case x3d_Replace:{currentColor.a=arg1.a;break;}case x3d_Modulate:{currentColor.a=arg1.a*arg2.a;break;}case x3d_Modulate2X:{currentColor.a=(arg1.a*arg2.a)*2.;break;}case x3d_Modulate4X:{currentColor.a=(arg1.a*arg2.a)*4.;break;}case x3d_Add:{currentColor.a=arg1.a+arg2.a;break;}case x3d_AddSigned:{currentColor.a=arg1.a+arg2.a-.5;break;}case x3d_AddSigned2X:{currentColor.a=(arg1.a+arg2.a-.5)*2.;break;}case x3d_AddSmooth:{currentColor.a=arg1.a+(1.-arg1.a)*arg2.a;break;}case x3d_Subtract:{currentColor.a=arg1.a-arg2.a;break;}case x3d_BlendDiffuseAlpha:{currentColor.a=arg1.a*diffuseColor.a+arg2.a*(1.-diffuseColor.a);break;}case x3d_BlendTextureAlpha:{currentColor.a=arg1.a*arg1.a+arg2.a*(1.-arg1.a);break;}case x3d_BlendFactorAlpha:{currentColor.a=arg1.a*x3d_MultiTextureColor.a+arg2.a*(1.-x3d_MultiTextureColor.a);break;}case x3d_BlendCurrentAlpha:{currentColor.a=arg1.a*arg2.a+arg2.a*(1.-arg2.a);break;}case x3d_ModulateAlphaAddColor:{currentColor.a=arg1.a+arg1.a*arg2.a;break;}case x3d_ModulateInvAlphaAddColor:{currentColor.a=(1.-arg1.a)*arg2.a+arg1.a;break;}case x3d_ModulateInvColorAddAlpha:{currentColor.a=(1.-arg1.a)*arg2.a+arg1.a;break;}case x3d_DotProduct3:{currentColor.a=dot(arg1.rgb*2.-1.,arg2.rgb*2.-1.);break;}case x3d_SelectArg1:{currentColor.a=arg1.a;break;}case x3d_SelectArg2:{currentColor.a=arg2.a;break;}case x3d_Off:{break;}}}return currentColor;
#else
return diffuseColor*getTexture(0,0,0);
#endif
}
#endif
#if defined(X3D_TEXTURE_PROJECTION)
uniform x3d_TextureProjectorParameters x3d_TextureProjector[X3D_NUM_TEXTURE_PROJECTORS];uniform sampler2D x3d_TextureProjectorTexture[X3D_NUM_TEXTURE_PROJECTORS];vec4 getTextureProjectorTexture(const in int i,const in vec2 texCoord){vec4 textureColor=vec4(1);switch(i){
${Array .from ({ length: (/* inlined export .maxTextures */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_PROJECTORS > ${i}
case ${i}:
textureColor = texture (x3d_TextureProjectorTexture [${i}], texCoord);
#if defined (X3D_COLORSPACE_SRGB)
#if defined (X3D_TEXTURE_PROJECTOR_${i}_LINEAR)
textureColor = linearToSRGB (textureColor);
#endif
#elif defined (X3D_COLORSPACE_LINEAR)
#if !defined (X3D_TEXTURE_PROJECTOR_${i}_LINEAR)
textureColor = sRGBToLinear (textureColor);
#endif
#endif
break;
#endif
`) .join ("\n")}
}return textureColor;}vec3 getTextureProjectorColor(){vec3 currentColor=vec3(1);
#if defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D)
vec3 N=normal;
#else
vec3 N=frontFacing?normal:-normal;
#endif
for(int i=0;i<X3D_NUM_TEXTURE_PROJECTORS;++i){x3d_TextureProjectorParameters textureProjector=x3d_TextureProjector[i];vec4 texCoord=textureProjector.matrix*vec4(vertex,1.);texCoord.stp/=texCoord.q;texCoord.p=clamp(texCoord.p,textureProjector.params.x,textureProjector.params.y);if(any(greaterThan(abs(texCoord.stp-.5),vec3(.5))))continue;vec3 p=textureProjector.location-vertex;if(dot(N,p)<0.)continue;vec4 T=getTextureProjectorTexture(i,texCoord.st);currentColor*=mix(vec3(1),T.rgb*textureProjector.color,T.a*textureProjector.intensity);}return currentColor;}
#endif
`
;

/* harmony default export */ const Texture2_glsl = (Namespace/* default */.A .add ("Texture2.glsl", Texture2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Unlit2.glsl.js


const Unlit2_glsl_default_ = () => /* glsl */ `
#include<Fragment>
uniform x3d_UnlitMaterialParameters x3d_Material;
${MaterialTextures .texture ("x3d_EmissiveTexture", "rgba", "sRGB")}
vec4 getEmissiveColor(){float alpha=1.-x3d_Material.transparency;
#if defined(X3D_COLOR_MATERIAL)
vec4 emissiveColor=vec4(color.rgb,color.a*alpha);
#else
vec4 emissiveColor=vec4(x3d_Material.emissiveColor,alpha);
#endif
#if defined(X3D_EMISSIVE_TEXTURE)
emissiveColor*=getEmissiveTexture();
#elif defined(X3D_TEXTURE)
emissiveColor=getTextureColor(emissiveColor,vec4(vec3(1),alpha));
#endif
return emissiveColor;}vec4 getMaterialColor(const in vec4 fragCoord){vec4 finalColor=getEmissiveColor();
#if defined(X3D_TEXTURE_PROJECTION)
finalColor.rgb*=getTextureProjectorColor();
#endif
return finalColor;}`
;

/* harmony default export */ const Unlit2_glsl = (Namespace/* default */.A .add ("Unlit2.glsl", Unlit2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Utils2.glsl.js
const Utils2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_SKINNING)||defined(X3D_INSTANCING)
vec4 texelFetch(const in sampler2D _sampler,const in int index,const in int lod){int x=textureSize(_sampler,lod).x;ivec2 p=ivec2(index % x,index/x);vec4 t=texelFetch(_sampler,p,lod);return t;}
#endif
#if!defined(X3D_SKINNING)
#define getSkinVertex(vertex,normal,tangent)(vertex)
#define getSkinNormal(normal)(normal)
#define getSkinTangent(tangent)(tangent)
#endif
`
;

/* harmony default export */ const Utils2_glsl = (Namespace/* default */.A .add ("Utils2.glsl", Utils2_glsl_default_));
;// ./src/assets/shaders/webgl2/common/Vertex2.glsl.js


const Vertex2_glsl_default_ = () => /* glsl */ `
uniform mat4 x3d_ProjectionMatrix;uniform mat4 x3d_ModelViewMatrix;
#if defined(X3D_XR_SESSION)
uniform mat4 x3d_EyeMatrix;
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
in vec3 x3d_LineStipple;
#endif
#if defined(X3D_COLOR_MATERIAL)
in vec4 x3d_Color;
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
in vec4 x3d_TexCoord${i};
#endif
`) .join ("\n")}
#endif
#endif
in vec4 x3d_Vertex;
#if defined(X3D_COLOR_MATERIAL)
out vec4 color;
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
out vec4 texCoord${i};
#endif
`) .join ("\n")}
#endif
#endif
#if defined(X3D_NORMALS)
uniform mat3 x3d_NormalMatrix;in vec3 x3d_Normal;out vec3 normal;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
out vec3 localNormal;
#endif
#if defined(X3D_TANGENTS)
in vec4 x3d_Tangent;out mat3 TBN;
#else
vec4 x3d_Tangent;
#endif
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
flat out float lengthSoFar;flat out vec2 startPoint;out vec2 midPoint;
#endif
out vec3 vertex;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
out vec3 localVertex;
#endif
#include<Utils>
#include<Skin>
#include<Instancing>
#include<PointSize>
#include<Fog>
#include<Logarithmic>
void main(){vec4 tVertex=x3d_Vertex;
#if defined(X3D_NORMALS)
vec3 tNormal=x3d_Normal;vec3 tTangent=x3d_Tangent.xyz;tVertex=getInstanceVertex(getSkinVertex(tVertex,tNormal,tTangent));tNormal=getInstanceNormal(getSkinNormal(tNormal));
#if defined(X3D_TANGENTS)
tTangent=getInstanceNormal(getSkinTangent(tTangent));
#endif
#else
tVertex=getInstanceVertex(getSkinVertex(tVertex,vec3(0),vec3(0)));
#endif
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
localVertex=tVertex.xyz;
#endif
tVertex=x3d_ModelViewMatrix*tVertex;
#if defined(X3D_XR_SESSION)
tVertex=x3d_EyeMatrix*tVertex;
#endif
vertex=tVertex.xyz;
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
gl_PointSize=pointSize=getInstancePointSize(getPointSize(vertex));
#else
gl_PointSize=1.;
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
lengthSoFar=x3d_LineStipple.z;startPoint=x3d_LineStipple.xy;midPoint=x3d_LineStipple.xy;
#endif
#if defined(X3D_FOG)&&defined(X3D_FOG_COORDS)
fog();
#endif
#if defined(X3D_COLOR_MATERIAL)
color=x3d_Color;
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
${Array .from ({ length: (/* inlined export .maxTexCoords */4) }, (_, i) => /* glsl */ `
#if X3D_NUM_TEXTURE_COORDINATES > ${i}
texCoord${i} = getInstanceTexCoord (x3d_TexCoord${i}, gl_VertexID % 6);
#endif
`) .join ("\n")}
#endif
#endif
#if defined(X3D_NORMALS)
normal=x3d_NormalMatrix*tNormal;
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
localNormal=tNormal;
#endif
#if defined(X3D_TANGENTS)
vec3 tangent=x3d_NormalMatrix*tTangent;vec3 bitangent=cross(normal,tangent)*x3d_Tangent.w;TBN=mat3(tangent,bitangent,normal);
#endif
#endif
gl_Position=x3d_ProjectionMatrix*tVertex;
#if defined(X3D_LOGARITHMIC_DEPTH_BUFFER)
logarithmic(gl_Position);
#endif
}`
;

/* harmony default export */ const Vertex2_glsl = (Namespace/* default */.A .add ("Vertex2.glsl", Vertex2_glsl_default_));
;// ./src/assets/shaders/webgl2/pbr/BRDF2.glsl.js
const BRDF2_glsl_default_ = () => /* glsl */ `
const float M_PI=3.1415926535897932384626433832795;float max3(const in vec3 value){return max(max(value.x,value.y),value.z);}vec3 rgb_mix(const in vec3 base,const in vec3 layer,const in vec3 rgb_alpha){float rgb_alpha_max=max(rgb_alpha.r,max(rgb_alpha.g,rgb_alpha.b));return(1.-rgb_alpha_max)*base+rgb_alpha*layer;}vec3 F_Schlick(const in vec3 f0,const in vec3 f90,const in float VdotH){return f0+(f90-f0)*pow(clamp(1.-VdotH,0.,1.),5.);}float F_Schlick(const in float f0,const in float f90,const in float VdotH){float x=clamp(1.-VdotH,0.,1.);float x2=x*x;float x5=x*x2*x2;return f0+(f90-f0)*x5;}float F_Schlick(const in float f0,const in float VdotH){float f90=1.;return F_Schlick(f0,f90,VdotH);}vec3 F_Schlick(const in vec3 f0,const in float f90,const in float VdotH){float x=clamp(1.-VdotH,0.,1.);float x2=x*x;float x5=x*x2*x2;return f0+(f90-f0)*x5;}vec3 F_Schlick(const in vec3 f0,const in float VdotH){float f90=1.;return F_Schlick(f0,f90,VdotH);}vec3 Schlick_to_F0(const in vec3 f,const in vec3 f90,const in float VdotH){float x=clamp(1.-VdotH,0.,1.);float x2=x*x;float x5=clamp(x*x2*x2,0.,.9999);return(f-f90*x5)/(1.-x5);}float Schlick_to_F0(const in float f,const in float f90,const in float VdotH){float x=clamp(1.-VdotH,0.,1.);float x2=x*x;float x5=clamp(x*x2*x2,0.,.9999);return(f-f90*x5)/(1.-x5);}vec3 Schlick_to_F0(const in vec3 f,const in float VdotH){return Schlick_to_F0(f,vec3(1),VdotH);}float Schlick_to_F0(const in float f,const in float VdotH){return Schlick_to_F0(f,1.,VdotH);}float V_GGX(const in float NdotL,const in float NdotV,const in float alphaRoughness){float alphaRoughnessSq=alphaRoughness*alphaRoughness;float GGXV=NdotL*sqrt(NdotV*NdotV*(1.-alphaRoughnessSq)+alphaRoughnessSq);float GGXL=NdotV*sqrt(NdotL*NdotL*(1.-alphaRoughnessSq)+alphaRoughnessSq);float GGX=GGXV+GGXL;if(GGX>0.){return.5/GGX;}return 0.;}float D_GGX(const in float NdotH,const in float alphaRoughness){float alphaRoughnessSq=alphaRoughness*alphaRoughness;float f=(NdotH*NdotH)*(alphaRoughnessSq-1.)+1.;return alphaRoughnessSq/(M_PI*f*f);}
#if defined(X3D_LIGHTING)
vec3 BRDF_lambertian(const in vec3 diffuseColor){return diffuseColor/M_PI;}vec3 BRDF_specularGGX(const in float alphaRoughness,const in float NdotL,const in float NdotV,const in float NdotH){float Vis=V_GGX(NdotL,NdotV,alphaRoughness);float D=D_GGX(NdotH,alphaRoughness);return vec3(Vis*D);}
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
vec3 BRDF_lambertianIridescence(const in vec3 f0,const in vec3 f90,const in vec3 iridescenceFresnel,const in float iridescenceFactor,const in vec3 diffuseColor,const in float specularWeight,const in float VdotH){vec3 iridescenceFresnelMax=vec3(max(max(iridescenceFresnel.r,iridescenceFresnel.g),iridescenceFresnel.b));vec3 schlickFresnel=F_Schlick(f0,f90,VdotH);vec3 F=mix(schlickFresnel,iridescenceFresnelMax,iridescenceFactor);return(1.-specularWeight*F)*(diffuseColor/M_PI);}vec3 BRDF_specularGGXIridescence(const in vec3 f0,const in vec3 f90,const in vec3 iridescenceFresnel,const in float alphaRoughness,const in float iridescenceFactor,const in float specularWeight,const in float VdotH,const in float NdotL,const in float NdotV,const in float NdotH){vec3 F=mix(F_Schlick(f0,f90,VdotH),iridescenceFresnel,iridescenceFactor);float Vis=V_GGX(NdotL,NdotV,alphaRoughness);float D=D_GGX(NdotH,alphaRoughness);return specularWeight*F*Vis*D;}
#endif
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
float D_GGX_anisotropic(const in float NdotH,const in float TdotH,const in float BdotH,const in float anisotropy,const in float at,const in float ab){float a2=at*ab;vec3 f=vec3(ab*TdotH,at*BdotH,a2*NdotH);float w2=a2/dot(f,f);return a2*w2*w2/M_PI;}float V_GGX_anisotropic(const in float NdotL,const in float NdotV,const in float BdotV,const in float TdotV,const in float TdotL,const in float BdotL,const in float at,const in float ab){float GGXV=NdotL*length(vec3(at*TdotV,ab*BdotV,NdotV));float GGXL=NdotV*length(vec3(at*TdotL,ab*BdotL,NdotL));float v=.5/(GGXV+GGXL);return clamp(v,0.,1.);}vec3 BRDF_specularGGXAnisotropy(const in float alphaRoughness,const in float anisotropy,const in vec3 n,const in vec3 v,const in vec3 l,const in vec3 h,const in vec3 t,const in vec3 b){float at=mix(alphaRoughness,1.,anisotropy*anisotropy);float ab=clamp(alphaRoughness,.001,1.);float NdotL=clamp(dot(n,l),0.,1.);float NdotH=clamp(dot(n,h),.001,1.);float NdotV=dot(n,v);float V=V_GGX_anisotropic(NdotL,NdotV,dot(b,v),dot(t,v),dot(t,l),dot(b,l),at,ab);float D=D_GGX_anisotropic(NdotH,dot(t,h),dot(b,h),anisotropy,at,ab);return vec3(V*D);}
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
float lambdaSheenNumericHelper(const in float x,const in float alphaG){float oneMinusAlphaSq=(1.-alphaG)*(1.-alphaG);float a=mix(21.5473,25.3245,oneMinusAlphaSq);float b=mix(3.82987,3.32435,oneMinusAlphaSq);float c=mix(.19823,.16801,oneMinusAlphaSq);float d=mix(-1.97760,-1.27393,oneMinusAlphaSq);float e=mix(-4.32054,-4.85967,oneMinusAlphaSq);return a/(1.+b*pow(x,c))+d*x+e;}float lambdaSheen(const in float cosTheta,const in float alphaG){if(abs(cosTheta)<.5)return exp(lambdaSheenNumericHelper(cosTheta,alphaG));else return exp(2.*lambdaSheenNumericHelper(.5,alphaG)-lambdaSheenNumericHelper(1.-cosTheta,alphaG));}float V_Sheen(const in float NdotL,const in float NdotV,in float sheenRoughness){sheenRoughness=max(sheenRoughness,.000001);float alphaG=sheenRoughness*sheenRoughness;return clamp(1./((1.+lambdaSheen(NdotV,alphaG)+lambdaSheen(NdotL,alphaG))*(4.*NdotV*NdotL)),0.,1.);}float D_Charlie(in float sheenRoughness,const in float NdotH){sheenRoughness=max(sheenRoughness,.000001);float alphaG=sheenRoughness*sheenRoughness;float invR=1./alphaG;float cos2h=NdotH*NdotH;float sin2h=1.-cos2h;return(2.+invR)*pow(sin2h,invR*.5)/(2.*M_PI);}vec3 BRDF_specularSheen(const in vec3 sheenColor,const in float sheenRoughness,const in float NdotL,const in float NdotV,const in float NdotH){float sheenDistribution=D_Charlie(sheenRoughness,NdotH);float sheenVisibility=V_Sheen(NdotL,NdotV,sheenRoughness);return sheenColor*sheenDistribution*sheenVisibility;}
#endif
#endif
`
;

/* harmony default export */ const BRDF2_glsl = (Namespace/* default */.A .add ("BRDF2.glsl", BRDF2_glsl_default_));
;// ./src/assets/shaders/webgl2/pbr/IBL2.glsl.js
const IBL2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_USE_IBL)
uniform x3d_EnvironmentLightSourceParameters x3d_EnvironmentLightSource;vec3 getDiffuseLight(const in vec3 n){vec3 texCoord=x3d_EnvironmentLightSource.rotation*n;vec3 textureColor=texture(x3d_EnvironmentLightSource.diffuseTexture,texCoord).rgb;
#if defined(X3D_COLORSPACE_SRGB)
#if defined(X3D_ENVIRONMENT_DIFFUSE_TEXTURE_LINEAR)
textureColor=linearToSRGB(textureColor);
#endif
#else
#if!defined(X3D_ENVIRONMENT_DIFFUSE_TEXTURE_LINEAR)
textureColor=sRGBToLinear(textureColor);
#endif
#endif
return textureColor*x3d_EnvironmentLightSource.color*x3d_EnvironmentLightSource.intensity;}vec3 getSpecularLight(const in vec3 reflection,const in float lod){vec3 texCoord=x3d_EnvironmentLightSource.rotation*reflection;vec3 textureColor=textureLod(x3d_EnvironmentLightSource.specularTexture,texCoord,lod).rgb;
#if defined(X3D_COLORSPACE_SRGB)
#if defined(X3D_ENVIRONMENT_SPECULAR_TEXTURE_LINEAR)
textureColor=linearToSRGB(textureColor);
#endif
#else
#if!defined(X3D_ENVIRONMENT_SPECULAR_TEXTURE_LINEAR)
textureColor=sRGBToLinear(textureColor);
#endif
#endif
return textureColor*x3d_EnvironmentLightSource.color*x3d_EnvironmentLightSource.intensity;}
#if defined(X3D_SHEEN_MATERIAL_EXT)
vec3 getSheenLight(const in vec3 reflection,const in float lod){vec3 texCoord=x3d_EnvironmentLightSource.rotation*reflection;vec3 textureColor=textureLod(x3d_EnvironmentLightSource.sheenTexture,texCoord,lod).rgb;
#if defined(X3D_COLORSPACE_SRGB)
#if defined(X3D_ENVIRONMENT_SHEEN_TEXTURE_LINEAR)
textureColor=linearToSRGB(textureColor);
#endif
#else
#if!defined(X3D_ENVIRONMENT_SHEEN_TEXTURE_LINEAR)
textureColor=sRGBToLinear(textureColor);
#endif
#endif
return textureColor*x3d_EnvironmentLightSource.color*x3d_EnvironmentLightSource.intensity;}
#endif
vec3 getIBLGGXFresnel(const in vec3 n,const in vec3 v,const in float roughness,const in vec3 F0,const in float specularWeight){float NdotV=clamp(dot(n,v),0.,1.);vec2 brdfSamplePoint=clamp(vec2(NdotV,roughness),vec2(0),vec2(1));vec2 f_ab=texture(x3d_EnvironmentLightSource.ggxLUTTexture,brdfSamplePoint).rg;vec3 Fr=max(vec3(1.-roughness),F0)-F0;vec3 k_S=F0+Fr*pow(1.-NdotV,5.);vec3 FssEss=specularWeight*(k_S*f_ab.x+f_ab.y);float Ems=(1.-(f_ab.x+f_ab.y));vec3 F_avg=specularWeight*(F0+(1.-F0)/21.);vec3 FmsEms=Ems*FssEss*F_avg/(1.-F_avg*Ems);return FssEss+FmsEms;}vec3 getIBLRadianceGGX(const in vec3 n,const in vec3 v,const in float roughness){float NdotV=clamp(dot(n,v),0.,1.);float lod=roughness*float(x3d_EnvironmentLightSource.specularTextureLevels);vec3 reflection=normalize(reflect(-v,n));vec3 specularLight=getSpecularLight(reflection,lod);return specularLight;}
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
uniform sampler2D x3d_TransmissionSamplerEXT;vec3 getTransmissionSample(const in vec2 fragCoord,const in float roughness,const in float ior){int framebufferSize=max(x3d_Viewport.z,x3d_Viewport.w);float framebufferLod=log2(float(framebufferSize))*applyIorToRoughness(roughness,ior);vec3 transmittedLight=textureLod(x3d_TransmissionSamplerEXT,fragCoord,framebufferLod).rgb;return transmittedLight;}vec3 getIBLVolumeRefraction(const in vec3 n,const in vec3 v,const in float perceptualRoughness,const in vec3 baseColor,const in vec3 position,const in mat4 modelMatrix,const in mat4 projMatrix,const in float ior,const in float thickness,const in vec3 attenuationColor,const in float attenuationDistance,const in float dispersion){
#if defined(X3D_DISPERSION_MATERIAL_EXT)
float halfSpread=(ior-1.)*.025*dispersion;vec3 iors=vec3(ior-halfSpread,ior,ior+halfSpread);vec3 transmittedLight;float transmissionRayLength;for(int i=0;i<3;i++){vec3 transmissionRay=getVolumeTransmissionRay(n,v,thickness,iors[i],modelMatrix);transmissionRayLength=length(transmissionRay);vec3 refractedRayExit=position+transmissionRay;vec4 ndcPos=projMatrix*vec4(refractedRayExit,1.);vec2 refractionCoords=ndcPos.xy/ndcPos.w;refractionCoords+=1.;refractionCoords/=2.;transmittedLight[i]=getTransmissionSample(refractionCoords,perceptualRoughness,iors[i])[i];}
#else
vec3 transmissionRay=getVolumeTransmissionRay(n,v,thickness,ior,modelMatrix);float transmissionRayLength=length(transmissionRay);vec3 refractedRayExit=position+transmissionRay;vec4 ndcPos=projMatrix*vec4(refractedRayExit,1.);vec2 refractionCoords=ndcPos.xy/ndcPos.w;refractionCoords+=1.;refractionCoords/=2.;vec3 transmittedLight=getTransmissionSample(refractionCoords,perceptualRoughness,ior);
#endif
vec3 attenuatedColor=applyVolumeAttenuation(transmittedLight,transmissionRayLength,attenuationColor,attenuationDistance);return attenuatedColor*baseColor;}
#endif
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
vec3 getIBLRadianceAnisotropy(const in vec3 n,const in vec3 v,const in float roughness,const in float anisotropy,const in vec3 anisotropyDirection){float NdotV=clamp(dot(n,v),0.,1.);float tangentRoughness=mix(roughness,1.,anisotropy*anisotropy);vec3 anisotropicTangent=cross(anisotropyDirection,v);vec3 anisotropicNormal=cross(anisotropicTangent,anisotropyDirection);float bendFactor=1.-anisotropy*(1.-roughness);float bendFactorPow4=bendFactor*bendFactor*bendFactor*bendFactor;vec3 bentNormal=normalize(mix(anisotropicNormal,n,bendFactorPow4));float lod=roughness*float(x3d_EnvironmentLightSource.specularTextureLevels);vec3 reflection=normalize(reflect(-v,bentNormal));vec3 specularLight=getSpecularLight(reflection,lod);return specularLight;}
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
vec3 getIBLRadianceCharlie(const in vec3 n,const in vec3 v,const in float sheenRoughness,const in vec3 sheenColor){float NdotV=clamp(dot(n,v),0.,1.);float lod=sheenRoughness*float(x3d_EnvironmentLightSource.sheenTextureLevels);vec3 reflection=normalize(reflect(-v,n));vec2 brdfSamplePoint=clamp(vec2(NdotV,sheenRoughness),vec2(0),vec2(1));float brdf=texture(x3d_EnvironmentLightSource.charlieLUTTexture,brdfSamplePoint).b;vec3 sheenLight=getSheenLight(reflection,lod);return sheenLight*sheenColor*brdf;}
#endif
#endif
`
;

/* harmony default export */ const IBL2_glsl = (Namespace/* default */.A .add ("IBL2.glsl", IBL2_glsl_default_));
;// ./src/assets/shaders/webgl2/pbr/MaterialInfo2.glsl.js


const MaterialInfo2_glsl_default_ = () => /* glsl */ `
struct MaterialInfo{float ior;float perceptualRoughness;vec3 f0_dielectric;float alphaRoughness;float fresnel_w;vec3 f90;vec3 f90_dielectric;float metallic;vec3 baseColor;float sheenRoughnessFactor;vec3 sheenColorFactor;vec3 clearcoatF0;vec3 clearcoatF90;float clearcoatFactor;vec3 clearcoatNormal;float clearcoatRoughness;float specularWeight;float transmissionFactor;float thickness;vec3 attenuationColor;float attenuationDistance;float iridescenceFactor;float iridescenceIor;float iridescenceThickness;float diffuseTransmissionFactor;vec3 diffuseTransmissionColorFactor;vec3 anisotropicT;vec3 anisotropicB;float anisotropyStrength;float dispersion;vec3 multiscatterColor;};
#if defined(X3D_MATERIAL_METALLIC_ROUGHNESS)
${MaterialTextures .texture ("x3d_BaseTexture", "rgba", "linear")}
vec4 getBaseColor(){float alpha=1.-x3d_Material.transparency;vec4 baseColor=vec4(x3d_Material.baseColor,alpha);
#if defined(X3D_COLOR_MATERIAL)
baseColor*=color;
#endif
#if defined(X3D_BASE_TEXTURE)
baseColor*=getBaseTexture();
#elif defined(X3D_TEXTURE)
baseColor=getTextureColor(baseColor,vec4(vec3(1),alpha));
#endif
return baseColor;}
${MaterialTextures .texture ("x3d_MetallicRoughnessTexture")}
MaterialInfo getMetallicRoughnessInfo(in MaterialInfo info){info.metallic=x3d_Material.metallic;info.perceptualRoughness=x3d_Material.roughness;
#if defined(X3D_METALLIC_ROUGHNESS_TEXTURE)
vec4 mrSample=getMetallicRoughnessTexture();info.metallic*=mrSample.b;info.perceptualRoughness*=mrSample.g;
#endif
return info;}
#endif
${MaterialTextures .texture ("x3d_EmissiveTexture", "rgb", "linear")}
#if defined(X3D_EMISSIVE_STRENGTH_MATERIAL_EXT)
uniform float x3d_EmissiveStrengthEXT;
#endif
vec3 getEmissiveColor(){vec3 emissiveColor=x3d_Material.emissiveColor;
#if defined(X3D_EMISSIVE_STRENGTH_MATERIAL_EXT)
emissiveColor*=x3d_EmissiveStrengthEXT;
#endif
#if defined(X3D_EMISSIVE_TEXTURE)
emissiveColor*=getEmissiveTexture();
#endif
return emissiveColor;}
${MaterialTextures .texture ("x3d_OcclusionTexture", "r")}
float getOcclusionFactor(){
#if defined(X3D_OCCLUSION_TEXTURE)
return getOcclusionTexture();
#else
return 1.;
#endif
}
#if defined(X3D_IOR_MATERIAL_EXT)
uniform float x3d_IorEXT;MaterialInfo getIorInfo(in MaterialInfo info){info.f0_dielectric=vec3(pow((x3d_IorEXT-1.)/(x3d_IorEXT+1.),2.));info.ior=x3d_IorEXT;return info;}
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_SheenColorTextureEXT",
"rgb", "linear")}
${MaterialTextures .texture ("x3d_SheenRoughnessTextureEXT", "a")}
uniform vec3 x3d_SheenColorEXT;uniform float x3d_SheenRoughnessEXT;MaterialInfo getSheenInfo(in MaterialInfo info){info.sheenColorFactor=x3d_SheenColorEXT;info.sheenRoughnessFactor=x3d_SheenRoughnessEXT;
#if defined(X3D_SHEEN_COLOR_TEXTURE_EXT)
info.sheenColorFactor*=getSheenColorTextureEXT();
#endif
#if defined(X3D_SHEEN_ROUGHNESS_TEXTURE_EXT)
info.sheenRoughnessFactor*=getSheenRoughnessTextureEXT();
#endif
return info;}uniform sampler2D x3d_SheenELUTTextureEXT;float albedoSheenScalingLUT(const in float NdotV,const in float sheenRoughnessFactor){return texture(x3d_SheenELUTTextureEXT,vec2(NdotV,sheenRoughnessFactor)).r;}
#endif
#if defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_DiffuseTransmissionTextureEXT",
"a")}
${MaterialTextures .texture ("x3d_DiffuseTransmissionColorTextureEXT", "rgb", "linear")}
uniform float x3d_DiffuseTransmissionEXT;uniform vec3 x3d_DiffuseTransmissionColorEXT;MaterialInfo getDiffuseTransmissionInfo(in MaterialInfo info){info.diffuseTransmissionFactor=x3d_DiffuseTransmissionEXT;info.diffuseTransmissionColorFactor=x3d_DiffuseTransmissionColorEXT;
#if defined(X3D_DIFFUSE_TRANSMISSION_TEXTURE_EXT)
info.diffuseTransmissionFactor*=getDiffuseTransmissionTextureEXT();
#endif
#if defined(X3D_DIFFUSE_TRANSMISSION_COLOR_TEXTURE_EXT)
info.diffuseTransmissionColorFactor*=getDiffuseTransmissionColorTextureEXT();
#endif
return info;}
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_ScatterStrengthTextureEXT", "a", "linear")}
${MaterialTextures .texture ("x3d_MultiscatterColorTextureEXT", "rgb", "linear")}
uniform float x3d_ScatterStrengthEXT;uniform vec3 x3d_MultiscatterColorEXT;MaterialInfo getVolumeScatterInfo(in MaterialInfo info){float scatterStrength=x3d_ScatterStrengthEXT;
#if defined(X3D_SCATTER_STRENGTH_TEXTURE_EXT)
scatterStrength*=getScatterStrengthTextureEXT();
#endif
info.multiscatterColor=x3d_MultiscatterColorEXT;
#if defined(X3D_MULTISCATTER_COLOR_TEXTURE_EXT)
info.multiscatterColor*=getMultiscatterColorTextureEXT();
#endif
return info;}
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_ClearcoatTextureEXT",
"r")}
${MaterialTextures .texture ("x3d_ClearcoatRoughnessTextureEXT", "g")}
${MaterialTextures .texture ("x3d_ClearcoatNormalTextureEXT",
"rgb")}
#if defined(X3D_CLEARCOAT_NORMAL_TEXTURE_EXT)
const float x3d_ClearcoatNormalScaleEXT=1.;
#endif
vec3 getClearcoatNormalEXT(const in NormalInfo normalInfo){
#if defined(X3D_CLEARCOAT_NORMAL_TEXTURE_EXT)
vec3 color=getClearcoatNormalTextureEXT();vec3 n=color*2.-vec3(1);n*=vec3(vec2(x3d_ClearcoatNormalScaleEXT),1.);n=mat3(normalInfo.t,normalInfo.b,normalInfo.ng)*normalize(n);return n;
#else
return normalInfo.ng;
#endif
}uniform float x3d_ClearcoatEXT;uniform float x3d_ClearcoatRoughnessEXT;MaterialInfo getClearCoatInfo(in MaterialInfo info,const in NormalInfo normalInfo){info.clearcoatFactor=x3d_ClearcoatEXT;info.clearcoatRoughness=x3d_ClearcoatRoughnessEXT;info.clearcoatF0=vec3(pow((info.ior-1.)/(info.ior+1.),2.));info.clearcoatF90=vec3(1);
#if defined(X3D_CLEARCOAT_TEXTURE_EXT)
info.clearcoatFactor*=getClearcoatTextureEXT();
#endif
#if defined(X3D_CLEARCOAT_ROUGHNESS_TEXTURE_EXT)
info.clearcoatRoughness*=getClearcoatRoughnessTextureEXT();
#endif
info.clearcoatNormal=getClearcoatNormalEXT(normalInfo);info.clearcoatRoughness=clamp(info.clearcoatRoughness,0.,1.);return info;}
#endif
#if defined(X3D_SPECULAR_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_SpecularTextureEXT",
"a")}
${MaterialTextures .texture ("x3d_SpecularColorTextureEXT", "rgb", "linear")}
uniform float x3d_SpecularEXT;uniform vec3 x3d_SpecularColorEXT;uniform float x3d_SpecularStrengthEXT;MaterialInfo getSpecularInfo(in MaterialInfo info){vec3 specularColor=x3d_SpecularStrengthEXT*x3d_SpecularColorEXT;vec4 specularTexture=vec4(1);
#if defined(X3D_SPECULAR_TEXTURE_EXT)
specularTexture.a=getSpecularTextureEXT();
#endif
#if defined(X3D_SPECULAR_COLOR_TEXTURE_EXT)
specularTexture.rgb=getSpecularColorTextureEXT();
#endif
info.f0_dielectric=min(info.f0_dielectric*specularColor*specularTexture.rgb,vec3(1));info.specularWeight=x3d_SpecularEXT*specularTexture.a;info.f90_dielectric=vec3(info.specularWeight);return info;}
#endif
#if defined(X3D_VOLUME_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_ThicknessTextureEXT", "g")}
uniform float x3d_ThicknessEXT;uniform float x3d_AttenuationDistanceEXT;uniform vec3 x3d_AttenuationColorEXT;MaterialInfo getVolumeInfo(in MaterialInfo info){info.thickness=x3d_ThicknessEXT;info.attenuationDistance=x3d_AttenuationDistanceEXT;info.attenuationColor=x3d_AttenuationColorEXT;
#if defined(X3D_THICKNESS_TEXTURE_EXT)
info.thickness*=getThicknessTextureEXT();
#endif
return info;}
#endif
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_IridescenceTextureEXT",
"r")}
${MaterialTextures .texture ("x3d_IridescenceThicknessTextureEXT", "g")}
uniform float x3d_IridescenceEXT;uniform float x3d_IridescenceIndexOfRefractionEXT;uniform float x3d_IridescenceThicknessMinimumEXT;uniform float x3d_IridescenceThicknessMaximumEXT;MaterialInfo getIridescenceInfo(in MaterialInfo info){info.iridescenceFactor=x3d_IridescenceEXT;info.iridescenceIor=x3d_IridescenceIndexOfRefractionEXT;info.iridescenceThickness=x3d_IridescenceThicknessMaximumEXT;
#if defined(X3D_IRIDESCENCE_TEXTURE_EXT)
info.iridescenceFactor*=getIridescenceTextureEXT();
#endif
#if defined(X3D_IRIDESCENCE_THICKNESS_TEXTURE_EXT)
float thicknessSampled=getIridescenceThicknessTextureEXT();float thickness=mix(x3d_IridescenceThicknessMinimumEXT,x3d_IridescenceThicknessMaximumEXT,thicknessSampled);info.iridescenceThickness=thickness;
#endif
return info;}
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_TransmissionTextureEXT", "r")}
uniform float x3d_TransmissionEXT;
#if defined(X3D_DISPERSION_MATERIAL_EXT)
uniform float x3d_DispersionEXT;
#endif
MaterialInfo getTransmissionInfo(in MaterialInfo info){info.transmissionFactor=x3d_TransmissionEXT;
#if defined(X3D_TRANSMISSION_TEXTURE_EXT)
info.transmissionFactor*=getTransmissionTextureEXT();
#endif
#if defined(X3D_DISPERSION_MATERIAL_EXT)
info.dispersion=x3d_DispersionEXT;
#else
info.dispersion=0.;
#endif
return info;}
#endif
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
${MaterialTextures .texture ("x3d_AnisotropyTextureEXT", "rgb")}
uniform vec3 x3d_AnisotropyEXT;MaterialInfo getAnisotropyInfo(in MaterialInfo info,const in NormalInfo normalInfo){vec2 direction=vec2(1.,0.);float strengthFactor=1.;
#if defined(X3D_ANISOTROPY_TEXTURE_EXT)
vec3 anisotropySample=getAnisotropyTextureEXT();direction=anisotropySample.xy*2.-vec2(1);strengthFactor=anisotropySample.z;
#endif
vec2 directionRotation=x3d_AnisotropyEXT.xy;mat2 rotationMatrix=mat2(directionRotation.xy,-directionRotation.y,directionRotation.x);direction=rotationMatrix*direction;info.anisotropicT=mat3(normalInfo.t,normalInfo.b,normalInfo.n)*normalize(vec3(direction,0.));info.anisotropicB=cross(normalInfo.ng,info.anisotropicT);info.anisotropyStrength=clamp(x3d_AnisotropyEXT.z*strengthFactor,0.,1.);return info;}
#endif
`
;

/* harmony default export */ const MaterialInfo2_glsl = (Namespace/* default */.A .add ("MaterialInfo2.glsl", MaterialInfo2_glsl_default_));
;// ./src/assets/shaders/webgl2/pbr/PBR2.glsl.js
const PBR2_glsl_default_ = () => /* glsl */ `
#include<Fragment>
#include<Shadow>
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)||defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
uniform ivec4 x3d_Viewport;
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)||defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)||defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
uniform mat4 x3d_ProjectionMatrix;uniform mat4 x3d_ModelViewMatrix;
#endif
#if defined(X3D_XR_SESSION)
uniform mat4 x3d_EyeMatrix;mat4 eye(const in mat4 modelViewMatrix){return x3d_EyeMatrix*modelViewMatrix;}
#else
#define eye(x)(x)
#endif
#if defined(X3D_LIGHTING)
uniform x3d_LightSourceParameters x3d_LightSource[X3D_NUM_LIGHTS];
#endif
uniform x3d_PhysicalMaterialParameters x3d_Material;
#include<BRDF>
#include<MaterialInfo>
#include<Punctual>
#include<IBL>
#include<Iridescence>
#if defined(X3D_VOLUME_SCATTER_PASS)
#include<Scatter>
#else
vec4 getMaterialColor(const in vec4 fragCoord){
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
mat4 modelViewMatrix=eye(x3d_ModelViewMatrix);
#endif
vec4 baseColor=getBaseColor();
#if defined(X3D_TEXTURE_PROJECTION)
baseColor.rgb*=getTextureProjectorColor();
#endif
vec3 color=vec3(0);vec3 v=normalize(-vertex);
#if defined(X3D_USE_IBL)||defined(X3D_LIGHTING)
NormalInfo normalInfo=getNormalInfo(x3d_Material.normalScale);vec3 n=normalInfo.n;float NdotV=clamp(dot(n,v),0.,1.);
#endif
MaterialInfo materialInfo;materialInfo.baseColor=baseColor.rgb;materialInfo.ior=1.5;materialInfo.f0_dielectric=vec3(.04);materialInfo.specularWeight=1.;materialInfo.f90=vec3(1);materialInfo.f90_dielectric=materialInfo.f90;
#if defined(X3D_IOR_MATERIAL_EXT)
materialInfo=getIorInfo(materialInfo);
#endif
#if defined(X3D_MATERIAL_METALLIC_ROUGHNESS)
materialInfo=getMetallicRoughnessInfo(materialInfo);
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
materialInfo=getSheenInfo(materialInfo);
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
materialInfo=getClearCoatInfo(materialInfo,normalInfo);
#endif
#if defined(X3D_SPECULAR_MATERIAL_EXT)
materialInfo=getSpecularInfo(materialInfo);
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
materialInfo=getTransmissionInfo(materialInfo);
#endif
#if defined(X3D_VOLUME_MATERIAL_EXT)
materialInfo=getVolumeInfo(materialInfo);
#endif
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
materialInfo=getIridescenceInfo(materialInfo);
#endif
#if defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
materialInfo=getDiffuseTransmissionInfo(materialInfo);
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
materialInfo=getVolumeScatterInfo(materialInfo);
#endif
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
materialInfo=getAnisotropyInfo(materialInfo,normalInfo);
#endif
materialInfo.perceptualRoughness=clamp(materialInfo.perceptualRoughness,0.,1.);materialInfo.metallic=clamp(materialInfo.metallic,0.,1.);materialInfo.alphaRoughness=materialInfo.perceptualRoughness*materialInfo.perceptualRoughness;vec3 f_specular_dielectric=vec3(0);vec3 f_specular_metal=vec3(0);vec3 f_diffuse=vec3(0);vec3 f_dielectric_brdf_ibl=vec3(0);vec3 f_metal_brdf_ibl=vec3(0);vec3 f_emissive=vec3(0);vec3 clearcoat_brdf=vec3(0);vec3 f_sheen=vec3(0);vec3 f_specular_transmission=vec3(0);vec3 f_diffuse_transmission=vec3(0);float clearcoatFactor=0.;vec3 clearcoatFresnel=vec3(0);float albedoSheenScaling=1.;float diffuseTransmissionThickness=1.;vec3 diffuseTransmissionIBL=vec3(0);
#if defined(X3D_USE_IBL)||defined(X3D_LIGHTING)
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
vec3 iridescenceFresnel_dielectric=evalIridescence(1.,materialInfo.iridescenceIor,NdotV,materialInfo.iridescenceThickness,materialInfo.f0_dielectric);vec3 iridescenceFresnel_metallic=evalIridescence(1.,materialInfo.iridescenceIor,NdotV,materialInfo.iridescenceThickness,baseColor.rgb);if(materialInfo.iridescenceThickness==0.)materialInfo.iridescenceFactor=0.;
#endif
#endif
#if defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
#if defined(X3D_VOLUME_MATERIAL_EXT)
diffuseTransmissionThickness=materialInfo.thickness*(length(x3d_ModelViewMatrix[0].xyz)+length(x3d_ModelViewMatrix[1].xyz)+length(x3d_ModelViewMatrix[2].xyz))/3.;
#endif
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
vec3 singleScatter=multiToSingleScatter(materialInfo.multiscatterColor);
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
clearcoatFactor=materialInfo.clearcoatFactor;clearcoatFresnel=F_Schlick(materialInfo.clearcoatF0,materialInfo.clearcoatF90,clamp(dot(materialInfo.clearcoatNormal,v),0.,1.));
#endif
#if defined(X3D_USE_IBL)
f_diffuse=getDiffuseLight(n)*baseColor.rgb;
#if defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
diffuseTransmissionIBL=getDiffuseLight(-n)*materialInfo.diffuseTransmissionColorFactor;
#if defined(X3D_VOLUME_MATERIAL_EXT)
diffuseTransmissionIBL=applyVolumeAttenuation(diffuseTransmissionIBL,diffuseTransmissionThickness,materialInfo.attenuationColor,materialInfo.attenuationDistance);
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
diffuseTransmissionIBL*=(1.-singleScatter);
#endif
f_diffuse=mix(f_diffuse,diffuseTransmissionIBL,materialInfo.diffuseTransmissionFactor);
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
f_specular_transmission+=getIBLVolumeRefraction(n,v,materialInfo.perceptualRoughness,baseColor.rgb,vertex,modelViewMatrix,x3d_ProjectionMatrix,materialInfo.ior,materialInfo.thickness,materialInfo.attenuationColor,materialInfo.attenuationDistance,materialInfo.dispersion);f_diffuse=mix(f_diffuse,f_specular_transmission,materialInfo.transmissionFactor);
#endif
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
f_specular_metal=getIBLRadianceAnisotropy(n,v,materialInfo.perceptualRoughness,materialInfo.anisotropyStrength,materialInfo.anisotropicB);f_specular_dielectric=f_specular_metal;
#else
f_specular_metal=getIBLRadianceGGX(n,v,materialInfo.perceptualRoughness);f_specular_dielectric=f_specular_metal;
#endif
vec3 f_metal_fresnel_ibl=getIBLGGXFresnel(n,v,materialInfo.perceptualRoughness,baseColor.rgb,1.);f_metal_brdf_ibl=f_metal_fresnel_ibl*f_specular_metal;vec3 f_dielectric_fresnel_ibl=getIBLGGXFresnel(n,v,materialInfo.perceptualRoughness,materialInfo.f0_dielectric,materialInfo.specularWeight);f_dielectric_brdf_ibl=mix(f_diffuse,f_specular_dielectric,f_dielectric_fresnel_ibl);
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
f_metal_brdf_ibl=mix(f_metal_brdf_ibl,f_specular_metal*iridescenceFresnel_metallic,materialInfo.iridescenceFactor);f_dielectric_brdf_ibl=mix(f_dielectric_brdf_ibl,rgb_mix(f_diffuse,f_specular_dielectric,iridescenceFresnel_dielectric),materialInfo.iridescenceFactor);
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
clearcoat_brdf=getIBLRadianceGGX(materialInfo.clearcoatNormal,v,materialInfo.clearcoatRoughness);
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
f_sheen=getIBLRadianceCharlie(n,v,materialInfo.sheenRoughnessFactor,materialInfo.sheenColorFactor);albedoSheenScaling=1.-max3(materialInfo.sheenColorFactor)*albedoSheenScalingLUT(NdotV,materialInfo.sheenRoughnessFactor);
#endif
color=mix(f_dielectric_brdf_ibl,f_metal_brdf_ibl,materialInfo.metallic);color=f_sheen+color*albedoSheenScaling;color=mix(color,clearcoat_brdf,clearcoatFactor*clearcoatFresnel);
#if defined(X3D_OCCLUSION_TEXTURE)
float ao=getOcclusionFactor();color*=1.+x3d_Material.occlusionStrength*(ao-1.);
#endif
#endif
f_diffuse=vec3(0);f_specular_dielectric=vec3(0);f_specular_metal=vec3(0);vec3 f_dielectric_brdf=vec3(0);vec3 f_metal_brdf=vec3(0);
#if defined(X3D_LIGHTING)
for(int i=0;i<X3D_NUM_LIGHTS;++i){x3d_LightSourceParameters light=x3d_LightSource[i];vec3 pointToLight;float distanceToLight;if(light.type!=x3d_DirectionalLight){pointToLight=light.location-vertex;distanceToLight=length(light.matrix*pointToLight);}else{pointToLight=-light.direction;distanceToLight=-1.;}if(distanceToLight<=light.radius||light.radius<0.){vec3 l=normalize(pointToLight);vec3 h=normalize(l+v);float NdotL=clamp(dot(n,l),0.,1.);float NdotH=clamp(dot(n,h),0.,1.);float VdotH=clamp(dot(v,h),0.,1.);vec3 dielectric_fresnel=F_Schlick(materialInfo.f0_dielectric*materialInfo.specularWeight,materialInfo.f90_dielectric,abs(VdotH));vec3 metal_fresnel=F_Schlick(baseColor.rgb,vec3(1),abs(VdotH));vec3 lightIntensity=getLightIntensity(light,l,distanceToLight);vec3 l_diffuse=lightIntensity*NdotL*BRDF_lambertian(baseColor.rgb);vec3 l_specular_dielectric=vec3(0);vec3 l_specular_metal=vec3(0);vec3 l_dielectric_brdf=vec3(0);vec3 l_metal_brdf=vec3(0);vec3 l_clearcoat_brdf=vec3(0);vec3 l_sheen=vec3(0);float l_albedoSheenScaling=1.;vec3 l_diffuse_btdf=vec3(0);
#if defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
l_diffuse*=1.-materialInfo.diffuseTransmissionFactor;if(dot(n,l)<0.){float diffuseNdotL=clamp(dot(-n,l),0.,1.);l_diffuse_btdf=lightIntensity*diffuseNdotL*BRDF_lambertian(materialInfo.diffuseTransmissionColorFactor);vec3 l_mirror=normalize(reflect(l,n));float diffuseVdotH=clamp(dot(v,normalize(l_mirror+v)),0.,1.);dielectric_fresnel=F_Schlick(materialInfo.f0_dielectric*materialInfo.specularWeight,materialInfo.f90_dielectric,abs(diffuseVdotH));
#if defined(X3D_VOLUME_MATERIAL_EXT)
l_diffuse_btdf=applyVolumeAttenuation(l_diffuse_btdf,diffuseTransmissionThickness,materialInfo.attenuationColor,materialInfo.attenuationDistance);
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
l_diffuse_btdf*=(1.-singleScatter);
#endif
l_diffuse+=l_diffuse_btdf*materialInfo.diffuseTransmissionFactor;}
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)
vec3 transmissionRay=getVolumeTransmissionRay(n,v,materialInfo.thickness,materialInfo.ior,modelViewMatrix);pointToLight-=transmissionRay;l=normalize(pointToLight);vec3 transmittedLight=lightIntensity*getPunctualRadianceTransmission(n,v,l,materialInfo.alphaRoughness,baseColor.rgb,materialInfo.ior);
#if defined(X3D_VOLUME_MATERIAL_EXT)
transmittedLight=applyVolumeAttenuation(transmittedLight,length(transmissionRay),materialInfo.attenuationColor,materialInfo.attenuationDistance);
#endif
l_diffuse=mix(l_diffuse,transmittedLight,materialInfo.transmissionFactor);
#endif
vec3 intensity=getLightIntensity(light,l,length(light.matrix*pointToLight));
#if defined(X3D_ANISOTROPY_MATERIAL_EXT)
l_specular_metal=intensity*NdotL*BRDF_specularGGXAnisotropy(materialInfo.alphaRoughness,materialInfo.anisotropyStrength,n,v,l,h,materialInfo.anisotropicT,materialInfo.anisotropicB);l_specular_dielectric=l_specular_metal;
#else
l_specular_metal=intensity*NdotL*BRDF_specularGGX(materialInfo.alphaRoughness,NdotL,NdotV,NdotH);l_specular_dielectric=l_specular_metal;
#endif
l_metal_brdf=metal_fresnel*l_specular_metal;l_dielectric_brdf=mix(l_diffuse,l_specular_dielectric,dielectric_fresnel);
#if defined(X3D_IRIDESCENCE_MATERIAL_EXT)
l_metal_brdf=mix(l_metal_brdf,l_specular_metal*iridescenceFresnel_metallic,materialInfo.iridescenceFactor);l_dielectric_brdf=mix(l_dielectric_brdf,rgb_mix(l_diffuse,l_specular_dielectric,iridescenceFresnel_dielectric),materialInfo.iridescenceFactor);
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
l_clearcoat_brdf=intensity*getPunctualRadianceClearCoat(materialInfo.clearcoatNormal,v,l,h,VdotH,materialInfo.clearcoatF0,materialInfo.clearcoatF90,materialInfo.clearcoatRoughness);
#endif
#if defined(X3D_SHEEN_MATERIAL_EXT)
l_sheen=intensity*getPunctualRadianceSheen(materialInfo.sheenColorFactor,materialInfo.sheenRoughnessFactor,NdotL,NdotV,NdotH);l_albedoSheenScaling=min(1.-max3(materialInfo.sheenColorFactor)*albedoSheenScalingLUT(NdotV,materialInfo.sheenRoughnessFactor),1.-max3(materialInfo.sheenColorFactor)*albedoSheenScalingLUT(NdotL,materialInfo.sheenRoughnessFactor));
#endif
vec3 l_color=mix(l_dielectric_brdf,l_metal_brdf,materialInfo.metallic);l_color=l_sheen+l_color*l_albedoSheenScaling;l_color=mix(l_color,l_clearcoat_brdf,clearcoatFactor*clearcoatFresnel);
#if defined(X3D_SHADOWS)
l_color=mix(l_color,light.shadowColor,getShadowIntensity(i,light));
#endif
color+=l_color;}}
#endif
#if defined(X3D_VOLUME_SCATTER_MATERIAL_EXT)
vec3 l_color=getSubsurfaceScattering(vertex,x3d_ProjectionMatrix,materialInfo.attenuationDistance,materialInfo.diffuseTransmissionColorFactor,materialInfo.multiscatterColor,fragCoord);color+=l_color*(1.-materialInfo.metallic)*(1.-clearcoatFactor*clearcoatFresnel)*(1.-materialInfo.iridescenceFactor)*(1.-materialInfo.transmissionFactor);
#endif
f_emissive=getEmissiveColor();
#if defined(X3D_UNLIT_MATERIAL_EXT)
color=baseColor.rgb;
#elif(defined(X3D_GEOMETRY_0D)||defined(X3D_GEOMETRY_1D))&&!defined(X3D_NORMALS)
color=f_emissive+baseColor.rgb;
#else
color=f_emissive*(1.-clearcoatFactor*clearcoatFresnel)+color;
#endif
return vec4(color,baseColor.a);}
#endif
`
;

/* harmony default export */ const PBR2_glsl = (Namespace/* default */.A .add ("PBR2.glsl", PBR2_glsl_default_));
;// ./src/assets/shaders/webgl2/pbr/Punctual2.glsl.js
const Punctual2_glsl_default_ = () => /* glsl */ `
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)||defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
float applyIorToRoughness(const in float roughness,const in float ior){return roughness*clamp(ior*2.-2.,0.,1.);}
#endif
#if defined(X3D_LIGHTING)
#include<Lighting>
float getRangeAttenuation(const in float radius,const in float distanceToLight){if(radius<=0.){return 1./pow(distanceToLight,2.);}return max(min(1.-pow(distanceToLight/radius,4.),1.),0.)/pow(distanceToLight,2.);}float getSpotAttenuation(const in vec3 pointToLight,const in vec3 spotDirection,const in float outerConeCos,const in float innerConeCos){float actualCos=dot(normalize(spotDirection),normalize(-pointToLight));if(actualCos>outerConeCos){if(actualCos<innerConeCos){float angularAttenuation=(actualCos-outerConeCos)/(innerConeCos-outerConeCos);return angularAttenuation*angularAttenuation;}return 1.;}return 0.;}vec3 getLightIntensity(const in x3d_LightSourceParameters light,const in vec3 pointToLight,const in float distanceToLight){float intensity=light.intensity;if(light.type!=x3d_DirectionalLight){intensity*=getAttenuation(light.attenuation,distanceToLight);intensity*=getRangeAttenuation(light.radius,distanceToLight);}if(light.type==x3d_SpotLight){intensity*=getSpotAttenuation(pointToLight,light.direction,cos(light.cutOffAngle),cos(light.beamWidth));}return intensity*light.color;}
#if defined(X3D_SHEEN_MATERIAL_EXT)
vec3 getPunctualRadianceSheen(const in vec3 sheenColor,const in float sheenRoughness,const in float NdotL,const in float NdotV,const in float NdotH){return NdotL*BRDF_specularSheen(sheenColor,sheenRoughness,NdotL,NdotV,NdotH);}
#endif
#if defined(X3D_CLEARCOAT_MATERIAL_EXT)
vec3 getPunctualRadianceClearCoat(const in vec3 clearcoatNormal,const in vec3 v,const in vec3 l,const in vec3 h,const in float VdotH,const in vec3 f0,const in vec3 f90,const in float clearcoatRoughness){float NdotL=clamp(dot(clearcoatNormal,l),0.,1.);float NdotV=clamp(dot(clearcoatNormal,v),0.,1.);float NdotH=clamp(dot(clearcoatNormal,h),0.,1.);return NdotL*BRDF_specularGGX(clearcoatRoughness*clearcoatRoughness,NdotL,NdotV,NdotH);}
#endif
#endif
#if defined(X3D_TRANSMISSION_MATERIAL_EXT)||defined(X3D_DIFFUSE_TRANSMISSION_MATERIAL_EXT)
vec3 getPunctualRadianceTransmission(const in vec3 normal,const in vec3 view,const in vec3 pointToLight,const in float alphaRoughness,const in vec3 baseColor,const in float ior){float transmissionRoughness=applyIorToRoughness(alphaRoughness,ior);vec3 n=normalize(normal);vec3 v=normalize(view);vec3 l=normalize(pointToLight);vec3 l_mirror=normalize(l+2.*n*dot(-l,n));vec3 h=normalize(l_mirror+v);float D=D_GGX(clamp(dot(n,h),0.,1.),transmissionRoughness);float Vis=V_GGX(clamp(dot(n,l_mirror),0.,1.),clamp(dot(n,v),0.,1.),transmissionRoughness);return baseColor*D*Vis;}vec3 applyVolumeAttenuation(const in vec3 radiance,const in float transmissionDistance,const in vec3 attenuationColor,const in float attenuationDistance){if(attenuationDistance==0.){return radiance;}else{vec3 attenuationCoefficient=log(attenuationColor)/attenuationDistance;vec3 transmittance=exp(attenuationCoefficient*transmissionDistance);return transmittance*radiance;}}vec3 getVolumeTransmissionRay(const in vec3 n,const in vec3 v,const in float thickness,const in float ior,in mat4 modelMatrix){vec3 refractionVector=refract(-v,normalize(n),1./ior);vec3 modelScale;modelScale.x=length(modelMatrix[0].xyz);modelScale.y=length(modelMatrix[1].xyz);modelScale.z=length(modelMatrix[2].xyz);return normalize(refractionVector)*thickness*modelScale;}
#endif
#include<SubsurfaceScattering>
`
;

/* harmony default export */ const Punctual2_glsl = (Namespace/* default */.A .add ("Punctual2.glsl", Punctual2_glsl_default_));
;// ./src/assets/shaders/webgl2/Default2.vs.js
const Default2_vs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;precision highp sampler3D;precision highp samplerCube;
#include<Vertex>
`
;

/* harmony default export */ const Default2_vs = (Namespace/* default */.A .add ("Default2.vs", Default2_vs_default_));
;// ./src/assets/shaders/webgl2/Depth2.fs.js
const Depth2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;uniform int x3d_Id;layout(location=0)out vec4 x3d_FragData0;
#if defined(X3D_CLIP_PLANES)
in vec3 vertex;
#endif
#if defined(X3D_NORMAL_BUFFER)
#if defined(X3D_NORMALS)
in vec3 normal;
#else
const vec3 normal=vec3(0.,0.,1.);
#endif
layout(location=1)out vec4 x3d_FragData1;
#endif
#include<ClipPlanes>
#include<Point>
void main(){
#if defined(X3D_CLIP_PLANES)
clip();
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
#if!(defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES))
if(getPointColor(vec4(1),gl_PointCoord).a<.5)discard;
#endif
#endif
#if defined(X3D_NORMAL_BUFFER)
x3d_FragData0=vec4(gl_FragCoord.z,vec3(x3d_Id));x3d_FragData1=vec4(normal,float(gl_FrontFacing));
#else
x3d_FragData0=vec4(vec3(gl_FragCoord.z),1.);
#endif
}`
;

/* harmony default export */ const Depth2_fs = (Namespace/* default */.A .add ("Depth2.fs", Depth2_fs_default_));
;// ./src/assets/shaders/webgl2/Depth2.vs.js
const Depth2_vs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;uniform mat4 x3d_ProjectionMatrix;uniform mat4 x3d_ModelViewMatrix;in vec4 x3d_Vertex;
#if defined(X3D_CLIP_PLANES)
out vec3 vertex;
#endif
#if defined(X3D_NORMAL_BUFFER)
#if defined(X3D_NORMALS)
in vec3 x3d_Normal;
#endif
out vec3 normal;
#endif
#include<Utils>
#include<Skin>
#include<Instancing>
#include<PointSize>
void main(){vec4 tVertex=x3d_Vertex;
#if defined(X3D_NORMALS)
vec3 tNormal=x3d_Normal;tVertex=getInstanceVertex(getSkinVertex(tVertex,tNormal,vec3(0)));tNormal=getInstanceNormal(getSkinNormal(tNormal));
#else
tVertex=getInstanceVertex(getSkinVertex(tVertex,vec3(0),vec3(0)));
#endif
tVertex=x3d_ModelViewMatrix*tVertex;
#if defined(X3D_CLIP_PLANES)
vertex=tVertex.xyz;
#endif
#if defined(X3D_NORMALS)
normal=tNormal;
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
gl_PointSize=pointSize=getPointSize(tVertex.xyz);
#else
gl_PointSize=1.;
#endif
gl_Position=x3d_ProjectionMatrix*tVertex;}`
;

/* harmony default export */ const Depth2_vs = (Namespace/* default */.A .add ("Depth2.vs", Depth2_vs_default_));
;// ./src/assets/shaders/webgl2/FullScreen2.vs.js
const FullScreen2_vs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;in vec2 x3d_Vertex;out vec2 texCoord;void main(){texCoord=x3d_Vertex;gl_Position=vec4(x3d_Vertex,0.,1.);}`
;

/* harmony default export */ const FullScreen2_vs = (Namespace/* default */.A .add ("FullScreen2.vs", FullScreen2_vs_default_));
;// ./src/assets/shaders/webgl2/LineTransform2.fs.js
const LineTransform2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;void main(){}`
;

/* harmony default export */ const LineTransform2_fs = (Namespace/* default */.A .add ("LineTransform2.fs", LineTransform2_fs_default_));
;// ./src/assets/shaders/webgl2/LineTransform2.vs.js
const LineTransform2_vs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;
#if defined(X3D_PASS_0)
uniform vec4 viewport;uniform mat4 modelViewProjectionMatrix;uniform mat4 invModelViewProjectionMatrix;uniform float linewidthScaleFactor1_2;
#if defined(X3D_INSTANCING)
in mat4 x3d_InstanceMatrix;
#endif
in float x3d_LengthSoFar;in uint x3d_CoordIndex0;in float x3d_FogDepth0;in vec4 x3d_Color0;in vec4 x3d_Vertex0;in uint x3d_CoordIndex1;in float x3d_FogDepth1;in vec4 x3d_Color1;in vec4 x3d_Vertex1;flat out uint coordIndex0;out vec3 lineStipple0;out float fogDepth0;out vec4 color0;out vec4 vertex0;flat out uint coordIndex1;out vec3 lineStipple1;out float fogDepth1;out vec4 color1;out vec4 vertex1;flat out uint coordIndex2;out vec3 lineStipple2;out float fogDepth2;out vec4 color2;out vec4 vertex2;vec3 projectPoint(const in vec4 point,const in mat4 modelViewProjectionMatrix,const in vec4 viewport){vec4 vin=modelViewProjectionMatrix*point;vin.xyz=vin.xyz/(2.*vin.w)+.5;return vec3(vin.xy*viewport.zw+viewport.xy,vin.z);}vec4 unProjectPoint(const in vec3 win,const in mat4 invModelViewProjection,const in vec4 viewport){vec4 vin=vec4((win.xy-viewport.xy)/viewport.zw,win.z,1.);vin.xyz=vin.xyz*2.-1.;vin=invModelViewProjection*vin;return vec4(vin.xyz/vin.w,1.);}void main(){vec4 inVertex0=x3d_Vertex0;vec4 inVertex1=x3d_Vertex1;
#if defined(X3D_INSTANCING)
inVertex0=x3d_InstanceMatrix*inVertex0;inVertex1=x3d_InstanceMatrix*inVertex1;
#endif
vec3 projected0=projectPoint(inVertex0,modelViewProjectionMatrix,viewport);vec3 projected1=projectPoint(inVertex1,modelViewProjectionMatrix,viewport);float neg0=projected0.z>1.?-1.:1.;float neg1=projected1.z>1.?-1.:1.;vec2 direction=normalize(projected1.xy-projected0.xy);vec2 offset=vec2(-direction.y,direction.x)*linewidthScaleFactor1_2;vec3 l0=vec3(projected1.xy,x3d_LengthSoFar);vec3 l1=vec3(projected0.xy,x3d_LengthSoFar);mat4 unProjectMatrix=invModelViewProjectionMatrix;
#if defined(X3D_INSTANCING)
unProjectMatrix=inverse(x3d_InstanceMatrix)*unProjectMatrix;
#endif
if(gl_InstanceID % 2==0){vec2 pq0=projected0.xy+offset*neg1;vec2 pq1=projected0.xy-offset*neg1;vec2 pq2=projected1.xy-offset*neg1;vec4 p0=unProjectPoint(vec3(pq0.xy,projected0.z),unProjectMatrix,viewport);vec4 p1=unProjectPoint(vec3(pq1.xy,projected0.z),unProjectMatrix,viewport);vec4 p2=unProjectPoint(vec3(pq2.xy,projected1.z),unProjectMatrix,viewport);coordIndex0=x3d_CoordIndex0;lineStipple0=l0;fogDepth0=x3d_FogDepth0;color0=x3d_Color0;vertex0=p0;coordIndex1=x3d_CoordIndex0;lineStipple1=l0;fogDepth1=x3d_FogDepth0;color1=x3d_Color0;vertex1=p1;coordIndex2=x3d_CoordIndex1;lineStipple2=l1;fogDepth2=x3d_FogDepth1;color2=x3d_Color1;vertex2=p2;}else{vec2 pq0=projected0.xy+offset*neg0;vec2 pq2=projected1.xy-offset*neg0;vec2 pq3=projected1.xy+offset*neg0;vec4 p0=unProjectPoint(vec3(pq0.xy,projected0.z),unProjectMatrix,viewport);vec4 p2=unProjectPoint(vec3(pq2.xy,projected1.z),unProjectMatrix,viewport);vec4 p3=unProjectPoint(vec3(pq3.xy,projected1.z),unProjectMatrix,viewport);coordIndex0=x3d_CoordIndex0;lineStipple0=l0;fogDepth0=x3d_FogDepth0;color0=x3d_Color0;vertex0=p0;coordIndex1=x3d_CoordIndex1;lineStipple1=l1;fogDepth1=x3d_FogDepth1;color1=x3d_Color1;vertex1=p2;coordIndex2=x3d_CoordIndex1;lineStipple2=l1;fogDepth2=x3d_FogDepth1;color2=x3d_Color1;vertex2=p3;}}
#endif
#if defined(X3D_PASS_1)
#if defined(X3D_INSTANCING)
in mat4 x3d_InstanceMatrix;
#endif
out mat4 instanceMatrix0;out mat4 instanceMatrix1;out mat4 instanceMatrix2;void main(){instanceMatrix0=x3d_InstanceMatrix;instanceMatrix1=x3d_InstanceMatrix;instanceMatrix2=x3d_InstanceMatrix;}
#endif
#if defined(X3D_PASS_2)
#if defined(X3D_INSTANCING)
in mat3 x3d_InstanceNormalMatrix;in vec3 x3d_Normal0;in vec3 x3d_Normal1;in vec4 x3d_Tangent0;in vec4 x3d_Tangent1;
#endif
out mat3 instanceNormalMatrix0;out vec4 tangent0;out vec3 normal0;out mat3 instanceNormalMatrix1;out vec4 tangent1;out vec3 normal1;out mat3 instanceNormalMatrix2;out vec4 tangent2;out vec3 normal2;void main(){if(gl_InstanceID % 2==0){instanceNormalMatrix0=x3d_InstanceNormalMatrix;tangent0=x3d_Tangent0;normal0=x3d_Normal0;instanceNormalMatrix1=x3d_InstanceNormalMatrix;tangent1=x3d_Tangent0;normal1=x3d_Normal0;instanceNormalMatrix2=x3d_InstanceNormalMatrix;tangent2=x3d_Tangent1;normal2=x3d_Normal1;}else{instanceNormalMatrix0=x3d_InstanceNormalMatrix;tangent0=x3d_Tangent0;normal0=x3d_Normal0;instanceNormalMatrix1=x3d_InstanceNormalMatrix;tangent1=x3d_Tangent1;normal1=x3d_Normal1;instanceNormalMatrix2=x3d_InstanceNormalMatrix;tangent2=x3d_Tangent1;normal2=x3d_Normal1;}}
#endif
`
;

/* harmony default export */ const LineTransform2_vs = (Namespace/* default */.A .add ("LineTransform2.vs", LineTransform2_vs_default_));
;// ./src/assets/shaders/webgl2/Material2.fs.js
const Material2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;precision highp sampler3D;precision highp samplerCube;
#include<Material>
`
;

/* harmony default export */ const Material2_fs = (Namespace/* default */.A .add ("Material2.fs", Material2_fs_default_));
;// ./src/assets/shaders/webgl2/OITCompose2.fs.js
const OITCompose2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;uniform sampler2D x3d_AccumRevealageTexture;uniform sampler2D x3d_AlphaTexture;out vec4 x3d_FragColor;void main(){ivec2 fragCoord=ivec2(gl_FragCoord.xy);vec4 accum=texelFetch(x3d_AccumRevealageTexture,fragCoord,0);if(accum.a>=1.)discard;float alpha=texelFetch(x3d_AlphaTexture,fragCoord,0).r;float revealage=1.-accum.a;x3d_FragColor=vec4(revealage*accum.rgb/clamp(alpha,.001,50000.),revealage);}`
;

/* harmony default export */ const OITCompose2_fs = (Namespace/* default */.A .add ("OITCompose2.fs", OITCompose2_fs_default_));
;// ./src/assets/shaders/webgl2/Physical2.fs.js
const Physical2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;precision highp sampler3D;precision highp samplerCube;
#include<PBR>
`
;

/* harmony default export */ const Physical2_fs = (Namespace/* default */.A .add ("Physical2.fs", Physical2_fs_default_));
;// ./src/assets/shaders/webgl2/Pointing2.fs.js
const Pointing2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;in vec3 vertex;in vec3 normal;in vec4 texCoord;
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
in vec4 texCoord0;
#else
vec4 texCoord0=vec4(0.,0.,0.,1.);
#endif
layout(location=0)out vec4 x3d_FragData0;layout(location=1)out vec4 x3d_FragData1;layout(location=2)out vec4 x3d_FragData2;
#include<ClipPlanes>
#include<Point>
#include<Stipple>
uniform float x3d_Id;void main(){
#if defined(X3D_CLIP_PLANES)
clip();
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
if(getPointColor(vec4(1),gl_PointCoord).a<.5)discard;texCoord0=vec4(gl_PointCoord.x,1.-gl_PointCoord.y,0.,1.);
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
stipple(gl_FragCoord);
#endif
x3d_FragData0=vec4(vertex,x3d_Id);x3d_FragData1=vec4(normal,0.);x3d_FragData2=texCoord0;}`
;

/* harmony default export */ const Pointing2_fs = (Namespace/* default */.A .add ("Pointing2.fs", Pointing2_fs_default_));
;// ./src/assets/shaders/webgl2/Pointing2.vs.js
const Pointing2_vs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;uniform mat4 x3d_ProjectionMatrix;uniform mat4 x3d_ModelViewMatrix;in vec4 x3d_Vertex;
#if defined(X3D_NORMALS)
in vec3 x3d_Normal;
#else
const vec3 x3d_Normal=vec3(0.,0.,1.);
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
in vec4 x3d_TexCoord0;
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
in vec3 x3d_LineStipple;
#endif
out vec3 vertex;out vec3 normal;
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
out vec4 texCoord0;
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
flat out float lengthSoFar;flat out vec2 startPoint;out vec2 midPoint;
#endif
#include<Utils>
#include<Skin>
#include<Instancing>
#include<PointSize>
void main(){
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
lengthSoFar=x3d_LineStipple.z;startPoint=x3d_LineStipple.xy;midPoint=x3d_LineStipple.xy;
#endif
vec4 tVertex=x3d_Vertex;tVertex=getInstanceVertex(getSkinVertex(tVertex,x3d_Normal,vec3(0)));tVertex=x3d_ModelViewMatrix*tVertex;vertex=tVertex.xyz;normal=getInstanceNormal(getSkinNormal(x3d_Normal));
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
gl_PointSize=pointSize=max(getPointSize(vertex),2.);
#else
gl_PointSize=2.;
#endif
#if!defined(X3D_GEOMETRY_0D)&&!defined(X3D_GEOMETRY_1D)
texCoord0=x3d_TexCoord0;
#endif
gl_Position=x3d_ProjectionMatrix*tVertex;}`
;

/* harmony default export */ const Pointing2_vs = (Namespace/* default */.A .add ("Pointing2.vs", Pointing2_vs_default_));
;// ./src/assets/shaders/webgl2/Unlit2.fs.js
const Unlit2_fs_default_ = () => /* glsl */ `#version 300 es
precision highp int;precision highp float;precision highp sampler2D;precision highp sampler3D;precision highp samplerCube;
#include<Unlit>
`
;

/* harmony default export */ const Unlit2_fs = (Namespace/* default */.A .add ("Unlit2.fs", Unlit2_fs_default_));
;// ./src/x_ite/Browser/Shaders/ShaderRegistry.js
/* WebGL 2 */





















/* PBR 2 */







/* WebGL 2 Vertex and Fragment */













const ShaderRegistry = {
   includes: {
      2: {
         ClipPlanes: ClipPlanes2_glsl,
         Fog: Fog2_glsl,
         Fragment: Fragment2_glsl,
         Hatch: Hatch2_glsl,
         Instancing: Instancing2_glsl,
         Lighting: Lighting2_glsl,
         Line2: Line22_glsl,
         Logarithmic: Logarithmic2,
         Material: Material2_glsl,
         Normal: Normal2_glsl,
         OIT: OIT2,
         Perlin: Perlin2_glsl,
         Point: Point2_glsl,
         PointSize: PointSize2_glsl,
         Shadow: Shadow2_glsl,
         Stipple: Stipple2_glsl,
         Texture: Texture2_glsl,
         Unlit: Unlit2_glsl,
         Utils: Utils2_glsl,
         Vertex: Vertex2_glsl,
         // PBR
         BRDF: BRDF2_glsl,
         IBL: IBL2_glsl,
         MaterialInfo: MaterialInfo2_glsl,
         PBR: PBR2_glsl,
         Punctual: Punctual2_glsl,
         ToneMapping: ToneMapping2_glsl,
      },
   },
   vertex: {
      2: {
         Default: Default2_vs,
         Depth: Depth2_vs,
         FullScreen: FullScreen2_vs,
         LineTransform: LineTransform2_vs,
         Pointing: Pointing2_vs,
      },
   },
   fragment: {
      2: {
         Depth: Depth2_fs,
         LineTransform: LineTransform2_fs,
         Material: Material2_fs,
         OITCompose: OITCompose2_fs,
         Physical: Physical2_fs,
         Pointing: Pointing2_fs,
         Unlit: Unlit2_fs,
      },
   },
   addVertexShader (name, shader2)
   {
      this .vertex [2] [name] = shader2;
   },
   addFragmentShader (name, shader2)
   {
      this .fragment [2] [name] = shader2;
   },
   addIncludeFile (name, shader2)
   {
      this .includes [2] [name] = shader2;
   },
};

const ShaderRegistry_default_ = ShaderRegistry;
;

/* harmony default export */ const Shaders_ShaderRegistry = (Namespace/* default */.A .add ("ShaderRegistry", ShaderRegistry_default_));
;// ./src/x_ite/Components/Shaders/X3DShaderNode.js




function X3DShaderNode (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DShaderNode);

   // Private properties

   this .valid    = false;
   this .selected = 0;
}

Object .assign (Object .setPrototypeOf (X3DShaderNode .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   setValid (value)
   {
      this ._isValid = this .valid = value;
   },
   isValid ()
   {
      return this .valid;
   },
   select ()
   {
      ++ this .selected;

      if (!this ._isSelected .getValue ())
         this ._isSelected = true;
   },
   deselect ()
   {
      -- this .selected;

      if (this .selected === 0)
      {
         if (this ._isSelected .getValue ())
            this ._isSelected = false;
      }
   },
});

Object .defineProperties (X3DShaderNode, Core_X3DNode .getStaticProperties ("X3DShaderNode", "Shaders", 1));

const X3DShaderNode_default_ = X3DShaderNode;
;

/* harmony default export */ const Shaders_X3DShaderNode = (Namespace/* default */.A .add ("X3DShaderNode", X3DShaderNode_default_));
;// ./src/x_ite/Components/Shaders/X3DProgrammableShaderObject.js
/* provided dependency */ var X3DProgrammableShaderObject_$ = __webpack_require__(44).A;







const X3DProgrammableShaderObject_uniformLocation = Symbol .for ("X_ITE.X3DField.uniformLocation");

function X3DProgrammableShaderObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DProgrammableShaderObject);

   this .addChildObjects (Base_X3DConstants .outputOnly, "renderedTextures", new x_ite_Fields .SFTime ());

   // Private properties

   this .attributeNames         = [ ];
   this .uniformNames           = [ ];
   this .environmentLightNodes  = [ ];
   this .lightNodes             = [ ];
   this .textureProjectorNodes  = [ ];
   this .textures               = new Set ();

   this .x3d_ClipPlane                  = [ ];
   this .x3d_EnvironmentLight           = [ ];
   this .x3d_Light                      = [ ];
   this .x3d_MultiTexture               = [ ];
   this .x3d_TexCoord                   = [ ];
   this .x3d_Texture                    = [ ];
   this .x3d_TextureCoordinateGenerator = [ ];
   this .x3d_TextureMatrix              = [ ];
   this .x3d_TextureProjector           = [ ];
}

Object .assign (X3DProgrammableShaderObject .prototype,
{
   initialize ()
   { },
   canUserDefinedFields ()
   {
      return true;
   },
   getAttributeNames ()
   {
      return this .attributeNames;
   },
   setAttributeNames (value)
   {
      this .attributeNames = value;
   },
   getUniformNames ()
   {
      return this .uniformNames;
   },
   setUniformNames (value)
   {
      this .uniformNames = value;
   },
   getDefaultUniformsAndAttributes ()
   {
      // Get uniforms and attributes.

      const
         program              = this .getProgram (),
         browser              = this .getBrowser (),
         gl                   = browser .getContext (),
         maxClipPlanes        = browser .getMaxClipPlanes (),
         maxEnvironmentLights = browser .getMaxEnvironmentLights (),
         maxLights            = browser .getMaxLights (),
         maxTextures          = browser .getMaxTextures (),
         maxTextureTransforms = browser .getMaxTextureTransforms (),
         maxTexCoords         = browser .getMaxTexCoords ();

      gl .useProgram (program);

      for (const name of this .uniformNames)
         this [name] = gl .getUniformLocation (program, name);

      /*
       * Uniforms.
       */

      for (let i = 0; i < maxClipPlanes; ++ i)
         this .x3d_ClipPlane [i] = gl .getUniformLocation (program, `x3d_ClipPlane[${i}]`);

      this .x3d_FogColor           = this .getUniformLocation (gl, program, "x3d_Fog.color",           "x3d_FogColor");
      this .x3d_FogVisibilityStart = this .getUniformLocation (gl, program, "x3d_Fog.visibilityStart", "x3d_FogVisibilityStart");
      this .x3d_FogVisibilityRange = this .getUniformLocation (gl, program, "x3d_Fog.visibilityRange", "x3d_FogVisibilityRange");
      this .x3d_FogMatrix          = this .getUniformLocation (gl, program, "x3d_Fog.matrix",          "x3d_FogMatrix");

      this .x3d_PointPropertiesPointSizeScaleFactor = gl .getUniformLocation (program, "x3d_PointProperties.pointSizeScaleFactor");
      this .x3d_PointPropertiesPointSizeMinValue    = gl .getUniformLocation (program, "x3d_PointProperties.pointSizeMinValue");
      this .x3d_PointPropertiesPointSizeMaxValue    = gl .getUniformLocation (program, "x3d_PointProperties.pointSizeMaxValue");
      this .x3d_PointPropertiesAttenuation          = gl .getUniformLocation (program, "x3d_PointProperties.attenuation");

      this .x3d_LinePropertiesLinetype = gl .getUniformLocation (program, "x3d_LineProperties.linetype");
      this .x3d_LineStippleScale       = gl .getUniformLocation (program, "x3d_LineProperties.lineStippleScale");
      this .x3d_LinePropertiesTexture  = gl .getUniformLocation (program, "x3d_LineProperties.texture");

      this .x3d_FillPropertiesFilled     = gl .getUniformLocation (program, "x3d_FillProperties.filled");
      this .x3d_FillPropertiesHatched    = gl .getUniformLocation (program, "x3d_FillProperties.hatched");
      this .x3d_FillPropertiesHatchColor = gl .getUniformLocation (program, "x3d_FillProperties.hatchColor");
      this .x3d_FillPropertiesTexture    = gl .getUniformLocation (program, "x3d_FillProperties.texture");
      this .x3d_FillPropertiesScale      = gl .getUniformLocation (program, "x3d_FillProperties.scale");

      for (let i = 0; i < maxEnvironmentLights; ++ i)
      {
         this .x3d_EnvironmentLight [i] = {
            color:                 gl .getUniformLocation (program, "x3d_EnvironmentLightSource.color"),
            intensity:             gl .getUniformLocation (program, "x3d_EnvironmentLightSource.intensity"),
            ambientIntensity:      gl .getUniformLocation (program, "x3d_EnvironmentLightSource.ambientIntensity"),
            rotation:              gl .getUniformLocation (program, "x3d_EnvironmentLightSource.rotation"),
            diffuseTexture:        gl .getUniformLocation (program, "x3d_EnvironmentLightSource.diffuseTexture"),
            diffuseTextureLevels:  gl .getUniformLocation (program, "x3d_EnvironmentLightSource.diffuseTextureLevels"),
            specularTexture:       gl .getUniformLocation (program, "x3d_EnvironmentLightSource.specularTexture"),
            specularTextureLevels: gl .getUniformLocation (program, "x3d_EnvironmentLightSource.specularTextureLevels"),
            sheenTexture:          gl .getUniformLocation (program, "x3d_EnvironmentLightSource.sheenTexture"),
            sheenTextureLevels:    gl .getUniformLocation (program, "x3d_EnvironmentLightSource.sheenTextureLevels"),
            ggxLUTTexture:         gl .getUniformLocation (program, "x3d_EnvironmentLightSource.ggxLUTTexture"),
            charlieLUTTexture:     gl .getUniformLocation (program, "x3d_EnvironmentLightSource.charlieLUTTexture"),
         };
      }

      for (let i = 0; i < maxLights; ++ i)
      {
         this .x3d_Light [i] = {
            type:             this .getUniformLocation (gl, program, `x3d_LightSource[${i}].type`,             `x3d_LightType[${i}]`),
            color:            this .getUniformLocation (gl, program, `x3d_LightSource[${i}].color`,            `x3d_LightColor[${i}]`),
            ambientIntensity: this .getUniformLocation (gl, program, `x3d_LightSource[${i}].ambientIntensity`, `x3d_LightAmbientIntensity[${i}]`),
            intensity:        this .getUniformLocation (gl, program, `x3d_LightSource[${i}].intensity`,        `x3d_LightIntensity[${i}]`),
            attenuation:      this .getUniformLocation (gl, program, `x3d_LightSource[${i}].attenuation`,      `x3d_LightAttenuation[${i}]`),
            location:         this .getUniformLocation (gl, program, `x3d_LightSource[${i}].location`,         `x3d_LightLocation[${i}]`),
            direction:        this .getUniformLocation (gl, program, `x3d_LightSource[${i}].direction`,        `x3d_LightDirection[${i}]`),
            beamWidth:        this .getUniformLocation (gl, program, `x3d_LightSource[${i}].beamWidth`,        `x3d_LightBeamWidth[${i}]`),
            cutOffAngle:      this .getUniformLocation (gl, program, `x3d_LightSource[${i}].cutOffAngle`,      `x3d_LightCutOffAngle[${i}]`),
            radius:           this .getUniformLocation (gl, program, `x3d_LightSource[${i}].radius`,           `x3d_LightRadius[${i}]`),
            matrix:           this .getUniformLocation (gl, program, `x3d_LightSource[${i}].matrix`,           `x3d_LightMatrix[${i}]`),

            shadowIntensity: gl .getUniformLocation (program, `x3d_LightSource[${i}].shadowIntensity`),
            shadowColor:     gl .getUniformLocation (program, `x3d_LightSource[${i}].shadowColor`),
            shadowBias:      gl .getUniformLocation (program, `x3d_LightSource[${i}].shadowBias`),
            shadowMatrix:    gl .getUniformLocation (program, `x3d_LightSource[${i}].shadowMatrix`),
            shadowMapSize:   gl .getUniformLocation (program, `x3d_LightSource[${i}].shadowMapSize`),
            shadowMap:       gl .getUniformLocation (program, `x3d_ShadowMap[${i}]`),
         };
      }

      this .x3d_AmbientIntensity  = this .getUniformLocation (gl, program, "x3d_Material.ambientIntensity", "x3d_FrontMaterial.ambientIntensity");
      this .x3d_DiffuseColor      = this .getUniformLocation (gl, program, "x3d_Material.diffuseColor",     "x3d_FrontMaterial.diffuseColor");
      this .x3d_SpecularColor     = this .getUniformLocation (gl, program, "x3d_Material.specularColor",    "x3d_FrontMaterial.specularColor");
      this .x3d_EmissiveColor     = this .getUniformLocation (gl, program, "x3d_Material.emissiveColor",    "x3d_FrontMaterial.emissiveColor");
      this .x3d_Shininess         = this .getUniformLocation (gl, program, "x3d_Material.shininess",        "x3d_FrontMaterial.shininess");
      this .x3d_BaseColor         = gl .getUniformLocation (program, "x3d_Material.baseColor");
      this .x3d_Metallic          = gl .getUniformLocation (program, "x3d_Material.metallic");
      this .x3d_Roughness         = gl .getUniformLocation (program, "x3d_Material.roughness");
      this .x3d_Glossiness        = gl .getUniformLocation (program, "x3d_Material.glossiness");
      this .x3d_OcclusionStrength = gl .getUniformLocation (program, "x3d_Material.occlusionStrength");
      this .x3d_NormalScale       = gl .getUniformLocation (program, "x3d_Material.normalScale");
      this .x3d_Transparency      = this .getUniformLocation (gl, program, "x3d_Material.transparency", "x3d_FrontMaterial.transparency");

      const commonUniforms = [
         // Matrices
         "x3d_Viewport",
         "x3d_ProjectionMatrix",
         "x3d_ModelViewMatrix",
         "x3d_EyeMatrix",
         "x3d_NormalMatrix",
         "x3d_ViewMatrix",
         "x3d_CameraSpaceMatrix",
         // ParticleSystem and Skinning
         "x3d_TexCoordRamp",
         "x3d_JointsTexture",
         "x3d_DisplacementsTexture",
         "x3d_DisplacementWeightsTexture",
         "x3d_JointMatricesTexture",
         // Common
         "x3d_Id", // Pointing ID
         "x3d_AlphaCutoff",
         "x3d_Exposure",
         "x3d_LogarithmicFarFactor1_2",
         "x3d_MultiTextureColor",
      ];

      for (const name of commonUniforms)
         this [name] = gl .getUniformLocation (program, name);

      for (const materialTexture of MaterialTextures .names)
      {
         this [materialTexture] = {
            textureTransformMapping:  gl .getUniformLocation (program, materialTexture + ".textureTransformMapping"),
            textureCoordinateMapping: gl .getUniformLocation (program, materialTexture + ".textureCoordinateMapping"),
            texture2D:                gl .getUniformLocation (program, materialTexture + ".texture2D"),
            texture3D:                gl .getUniformLocation (program, materialTexture + ".texture3D"),
            textureCube:              gl .getUniformLocation (program, materialTexture + ".textureCube"),
         };
      }

      for (let i = 0; i < maxTextures; ++ i)
      {
         this .x3d_Texture [i] = {
            texture2D: gl .getUniformLocation (program, `x3d_Texture2D[${i}]`),
            texture3D: gl .getUniformLocation (program, `x3d_Texture3D[${i}]`),
            textureCube: this .getUniformLocation (gl, program, `x3d_TextureCube[${i}]`, `x3d_CubeMapTexture[${i}]`),
         }

         this .x3d_MultiTexture [i] = {
            mode:      gl .getUniformLocation (program, `x3d_MultiTexture[${i}].mode`),
            alphaMode: gl .getUniformLocation (program, `x3d_MultiTexture[${i}].alphaMode`),
            source:    gl .getUniformLocation (program, `x3d_MultiTexture[${i}].source`),
            function:  gl .getUniformLocation (program, `x3d_MultiTexture[${i}].function`),
         };

         this .x3d_TextureProjector [i] = {
            color:     gl .getUniformLocation (program, `x3d_TextureProjector[${i}].color`),
            intensity: gl .getUniformLocation (program, `x3d_TextureProjector[${i}].intensity`),
            location:  gl .getUniformLocation (program, `x3d_TextureProjector[${i}].location`),
            params:    gl .getUniformLocation (program, `x3d_TextureProjector[${i}].params`),
            matrix:    gl .getUniformLocation (program, `x3d_TextureProjector[${i}].matrix`),
            texture:   gl .getUniformLocation (program, `x3d_TextureProjectorTexture[${i}]`),
         }
      }

      for (let i = 0; i < maxTextureTransforms; ++ i)
         this .x3d_TextureMatrix [i] = gl .getUniformLocation (program, `x3d_TextureMatrix[${i}]`);

      this .x3d_TexCoord .length = 0;

      for (let i = 0; i < maxTexCoords; ++ i)
      {
         this .x3d_TextureCoordinateGenerator [i] = {
            mode:      gl .getUniformLocation (program, `x3d_TextureCoordinateGenerator[${i}].mode`),
            parameter: gl .getUniformLocation (program, `x3d_TextureCoordinateGenerator[${i}].parameter`),
         };

         const x3d_TexCoord = this .getAttribLocation (gl, program, `x3d_TexCoord${i}`, i ? "" : "x3d_TexCoord");

         if (x3d_TexCoord !== -1)
            this .x3d_TexCoord .push ([i, x3d_TexCoord]);
      }

      /*
       * Attributes.
       */

      const attributes = [
         "CoordIndex",
         "LineStipple",
         "FogDepth",
         "Color",
         "Normal",
         "Tangent",
         "Vertex",
         "Particle",
         "ParticleVelocity",
         "ParticleMatrix",
         "ParticleNormalMatrix",
         "InstanceMatrix",
         "InstanceNormalMatrix",
      ];

      const attributeMappings = {
         "InstanceMatrix": ["ParticleMatrix"],
         "InstanceNormalMatrix": ["ParticleNormalMatrix"],
      };

      for (const name of attributes)
      {
         let attribute = gl .getAttribLocation (program, `x3d_${name}`);

         for (const alias of attributeMappings [name] ?? [ ])
         {
            if (attribute === -1)
               attribute = gl .getAttribLocation (program, `x3d_${alias}`);
            else
               break;
         }

         this [`x3d_${name}`] = attribute;

         if (attribute < 0)
         {
            this [`enable${name}Attribute`]                       = Function .prototype;
            this [`${X3DProgrammableShaderObject_$.toLowerCaseFirst (name)}AttributeDivisor`] = Function .prototype;
         }
         else
         {
            delete this [`enable${name}Attribute`];
            delete this [`${X3DProgrammableShaderObject_$.toLowerCaseFirst (name)}AttributeDivisor`];
         }
      }

      for (const name of this .attributeNames)
         this [name] = gl .getAttribLocation (program, name);

      if (this .x3d_TexCoord .length)
         delete this .enableTexCoordAttribute;
      else
         this .enableTexCoordAttribute  = Function .prototype;

      /*
       * Fill uniforms with defaults.
       */

      gl .uniform1f (this .x3d_Exposure, Math .max (browser .getBrowserOption ("Exposure"), 0));

      const
         texture2DUnit   = browser .getDefaultTexture2DUnit (),
         texture3DUnit   = browser .getDefaultTexture3DUnit (),
         textureCubeUnit = browser .getDefaultTextureCubeUnit ();

      for (const uniforms of this .x3d_Light)
         gl .uniform1i (uniforms .shadowMap, texture2DUnit);

      for (const uniform of this .x3d_Texture)
      {
         gl .uniform1i (uniform .texture2D,   texture2DUnit);
         gl .uniform1i (uniform .texture3D,   texture3DUnit);
         gl .uniform1i (uniform .textureCube, textureCubeUnit);
      }
   },
   getUniformLocation (gl, program, name, deprecated)
   {
      // Legacy function to get uniform location.

      const location = gl .getUniformLocation (program, name);

      if (location)
      {
         return location;
      }
      else
      {
         // Look for deprecated location.

         const location = gl .getUniformLocation (program, deprecated);

         if (location)
         {
            console .warn (this .getTypeName (), this .getName (), `Using uniform location name '${deprecated}' is deprecated, use '${name}'. See https://create3000.github.io/x_ite/custom-shaders.`);
         }

         return location;
      }
   },
   getAttribLocation (gl, program, name, deprecated)
   {
      // Legacy function to get uniform location.

      const location = gl .getAttribLocation (program, name);

      if (location >= 0)
      {
         return location;
      }
      else if (deprecated)
      {
         // Look for deprecated location.

         const location = gl .getAttribLocation (program, deprecated);

         if (location >= 0)
         {
            console .warn (this .getTypeName (), this .getName (), `Using attribute location name '${deprecated}' is deprecated, use '${name}'. See https://create3000.github.io/x_ite/custom-shaders.`);
         }

         return location;
      }
      else
      {
         return -1;
      }
   },
   addShaderFields ()
   {
      const
         gl      = this .getBrowser () .getContext (),
         program = this .getProgram ();

      gl .useProgram (program);

      this .textures .clear ();

      for (const field of this .getUserDefinedFields ())
      {
         if (field .getAccessType () === Base_X3DConstants .outputOnly)
            continue;

         const location = gl .getUniformLocation (program, field .getName ());

         if (!location)
            continue;

         switch (field .getType ())
         {
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .SFInt32:
            {
               location .uniform = gl .uniform1i;
               break;
            }
            case Base_X3DConstants .SFDouble:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFTime:
            {
               location .uniform = gl .uniform1f;
               break;
            }
            case Base_X3DConstants .SFImage:
            {
               location .array   = new Int32Array (3 + field .array .length);
               location .uniform = gl .uniform1iv;
               break;
            }
            case Base_X3DConstants .SFMatrix3d:
            case Base_X3DConstants .SFMatrix3f:
            case Base_X3DConstants .SFQuaternion:
            case Base_X3DConstants .SFRotation:
            {
               location .array   = new Float32Array (9);
               location .uniform = gl .uniformMatrix3fv;
               break;
            }
            case Base_X3DConstants .SFMatrix4d:
            case Base_X3DConstants .SFMatrix4f:
            {
               location .array   = new Float32Array (16);
               location .uniform = gl .uniformMatrix4fv;
               break;
            }
            case Base_X3DConstants .SFNode:
            {
               break;
            }
            case Base_X3DConstants .SFVec2d:
            case Base_X3DConstants .SFVec2f:
            {
               location .uniform = gl .uniform2f;
               break;
            }
            case Base_X3DConstants .SFColor:
            case Base_X3DConstants .SFVec3d:
            case Base_X3DConstants .SFVec3f:
            {
               location .uniform = gl .uniform3f;
               break;
            }
            case Base_X3DConstants .SFColorRGBA:
            case Base_X3DConstants .SFVec4d:
            case Base_X3DConstants .SFVec4f:
            {
               location .uniform = gl .uniform4f;
               break;
            }
            case Base_X3DConstants .MFBool:
            case Base_X3DConstants .MFInt32:
            {
               location .array   = new Int32Array (this .getLocationLength (gl, program, field));
               location .uniform = gl .uniform1iv;
               break;
            }
            case Base_X3DConstants .MFFloat:
            case Base_X3DConstants .MFDouble:
            case Base_X3DConstants .MFTime:
            {
               location .array   = new Float32Array (this .getLocationLength (gl, program, field));
               location .uniform = gl .uniform1fv;
               break;
            }
            case Base_X3DConstants .MFImage:
            {
               location .array   = new Int32Array (this .getImagesLength (field));
               location .uniform = gl .uniform1iv;
               break;
            }
            case Base_X3DConstants .MFMatrix3d:
            case Base_X3DConstants .MFMatrix3f:
            case Base_X3DConstants .MFQuaternion:
            case Base_X3DConstants .MFRotation:
            {
               location .array   = new Float32Array (9 * this .getLocationLength (gl, program, field));
               location .uniform = gl .uniformMatrix3fv;
               break;
            }
            case Base_X3DConstants .MFMatrix4d:
            case Base_X3DConstants .MFMatrix4f:
            {
               location .array   = new Float32Array (16 * this .getLocationLength (gl, program, field));
               location .uniform = gl .uniformMatrix4fv;
               break;
            }
            case Base_X3DConstants .MFNode:
            {
               const locations = location .locations = [ ];

               for (let i = 0;; ++ i)
               {
                  const l = gl .getUniformLocation (program, `${field .getName ()}[${i}]`);

                  if (!l)
                     break;

                  locations .push (l);
               }

               break;
            }
            case Base_X3DConstants .MFVec2d:
            case Base_X3DConstants .MFVec2f:
            {
               location .array   = new Float32Array (2 * this .getLocationLength (gl, program, field));
               location .uniform = gl .uniform2fv;
               break;
            }
            case Base_X3DConstants .MFColor:
            case Base_X3DConstants .MFVec3d:
            case Base_X3DConstants .MFVec3f:
            {
               location .array   = new Float32Array (3 * this .getLocationLength (gl, program, field));
               location .uniform = gl .uniform3fv;
               break;
            }
            case Base_X3DConstants .MFColorRGBA:
            case Base_X3DConstants .MFVec4d:
            case Base_X3DConstants .MFVec4f:
            {
               location .array   = new Float32Array (4 * this .getLocationLength (gl, program, field));
               location .uniform = gl .uniform4fv;
               break;
            }
         }

         if (location .array)
            field [X3DProgrammableShaderObject_uniformLocation] = location .array .length ? location : null;
         else
            field [X3DProgrammableShaderObject_uniformLocation] = location;

         if (!field [X3DProgrammableShaderObject_uniformLocation])
            continue;

         field .addInterest ("set_field__", this);

         this .set_field__ (field);
      }
   },
   removeShaderFields ()
   {
      for (const field of this .getUserDefinedFields ())
         field .removeInterest ("set_field__", this);
   },
   set_field__: (() =>
   {
      const rotationMatrix = new Float32Array (9);

      return function (field)
      {
         const
            gl       = this .getBrowser () .getContext (),
            program  = this .getProgram (),
            location = field [X3DProgrammableShaderObject_uniformLocation];

         gl .useProgram (program);

         switch (field .getType ())
         {
            case Base_X3DConstants .SFBool:
            case Base_X3DConstants .SFInt32:
            case Base_X3DConstants .SFDouble:
            case Base_X3DConstants .SFFloat:
            case Base_X3DConstants .SFTime:
            {
               location .uniform .call (gl, location, field .getValue ());
               return;
            }
            case Base_X3DConstants .SFImage:
            {
               const { array, uniform } = location;

               let a = 0;

               array [a ++] = field .width;
               array [a ++] = field .height;
               array [a ++] = field .comp;

               for (const pixel of field .array)
                  array [a ++] = pixel;

               uniform .call (gl, location, array);
               return;
            }
            case Base_X3DConstants .SFMatrix3d:
            case Base_X3DConstants .SFMatrix3f:
            case Base_X3DConstants .SFMatrix4d:
            case Base_X3DConstants .SFMatrix4f:
            {
               location .array .set (field .getValue ());
               location .uniform .call (gl, location, false, location .array);
               return;
            }
            case Base_X3DConstants .SFNode:
            {
               const textureNode = Base_X3DCast (Base_X3DConstants .X3DTextureNode, field);

               if (textureNode)
               {
                  location .name        = field .getName ();
                  location .textureNode = textureNode;

                  this .textures .add (location);
               }
               else
               {
                  this .textures .delete (location);
               }

               this ._renderedTextures = this .getBrowser () .getCurrentTime ();
               return;
            }
            case Base_X3DConstants .SFQuaternion:
            case Base_X3DConstants .SFRotation:
            {
               field .getValue () .getMatrix (location .array);

               location .uniform .call (gl, location, false, location .array);
               return;
            }
            case Base_X3DConstants .SFString:
            {
               return;
            }
            case Base_X3DConstants .SFVec2d:
            case Base_X3DConstants .SFVec2f:
            case Base_X3DConstants .SFColor:
            case Base_X3DConstants .SFVec3d:
            case Base_X3DConstants .SFVec3f:
            case Base_X3DConstants .SFColorRGBA:
            case Base_X3DConstants .SFVec4d:
            case Base_X3DConstants .SFVec4f:
            {
               location .uniform .call (gl, location, ... field .getValue ());
               return;
            }
            case Base_X3DConstants .MFBool:
            case Base_X3DConstants .MFInt32:
            case Base_X3DConstants .MFDouble:
            case Base_X3DConstants .MFFloat:
            case Base_X3DConstants .MFTime:
            {
               const { array, uniform } = location;

               let a = 0;

               for (const value of field)
                  array [a ++] = value;

               array .fill (0, a);

               uniform .call (gl, location, array);
               return;
            }
            case Base_X3DConstants .MFImage:
            {
               const { array, uniform } = location;

               let a = 0;

               for (const image of field)
               {
                  array [a ++] = image .width;
                  array [a ++] = image .height;
                  array [a ++] = image .comp;

                  for (const pixel of image .array)
                     array [a ++] = pixel;
               }

               array .fill (0, a);

               uniform .call (gl, location, array);
               return;
            }
            case Base_X3DConstants .MFMatrix3d:
            case Base_X3DConstants .MFMatrix3f:
            case Base_X3DConstants .MFMatrix4d:
            case Base_X3DConstants .MFMatrix4f:
            {
               const { array, uniform } = location;

               let a = 0;

               for (const matrix of field)
               {
                  for (const element of matrix)
                     array [a ++] = element;
               }

               array .fill (0, a);

               uniform .call (gl, location, false, array);
               return;
            }
            case Base_X3DConstants .MFNode:
            {
               const
                  locations   = location .locations,
                  fieldLength = field .length;

               for (let i = 0; i < fieldLength; ++ i)
               {
                  const textureNode = Base_X3DCast (Base_X3DConstants .X3DTextureNode, field [i]);

                  if (textureNode)
                  {
                     locations [i] .name        = field .getName ();
                     locations [i] .textureNode = textureNode;

                     this .textures .add (locations [i]);
                  }
                  else
                  {
                     this .textures .delete (locations [i]);
                  }
               }

               this ._renderedTextures = this .getBrowser () .getCurrentTime ();
               return;
            }
            case Base_X3DConstants .MFQuaternion:
            case Base_X3DConstants .MFRotation:
            {
               const { array, uniform } = location;

               let a = 0;

               for (const rotation of field)
               {
                  rotation .getValue () .getMatrix (rotationMatrix);

                  for (const element of rotationMatrix)
                     array [a ++] = element;
               }

               array .fill (0, a);

               uniform .call (gl, location, false, array);
               return;
            }
            case Base_X3DConstants .MFString:
            {
               return;
            }
            case Base_X3DConstants .MFVec2d:
            case Base_X3DConstants .MFVec2f:
            case Base_X3DConstants .MFColor:
            case Base_X3DConstants .MFVec3d:
            case Base_X3DConstants .MFVec3f:
            case Base_X3DConstants .MFColorRGBA:
            case Base_X3DConstants .MFVec4d:
            case Base_X3DConstants .MFVec4f:
            {
               const { array, uniform } = location;

               let a = 0;

               for (const vector of field)
               {
                  for (const element of vector)
                     array [a ++] = element;
               }

               array .fill (0, a);

               uniform .call (gl, location, array);
               return;
            }
         }
      };
   })(),
   getImagesLength (field)
   {
      const images = field .getValue ();

      let length = 3 * images .length;

      for (const image of images)
         length += image .array .length;

      return length;
   },
   getLocationLength (gl, program, field)
   {
      const name = field .getName ();

      for (let i = 0; ; ++ i)
      {
         const location = gl .getUniformLocation (program, `${name}[${i}]`);

         if (!location)
            return i;
      }
   },
   getRenderedTextures (renderedTextures)
   {
      for (const { textureNode } of this .textures)
         textureNode .getRenderedTextures (renderedTextures);
   },
   hasFog (fogNode)
   {
      if (this .fogNode === fogNode)
         return true;

      this .fogNode = fogNode;

      return false;
   },
   hasEnvironmentLight (i, lightNode)
   {
      if (this .environmentLightNodes [i] === lightNode)
         return true;

      this .environmentLightNodes [i] = lightNode;

      return false;
   },
   hasLight (i, lightNode)
   {
      if (this .lightNodes [i] === lightNode)
         return true;

      this .lightNodes [i] = lightNode;

      return false;
   },
   hasTextureProjector (i, textureProjectorNode)
   {
      if (this .textureProjectorNodes [i] === textureProjectorNode)
         return true;

      this .textureProjectorNodes [i] = textureProjectorNode;

      return false;
   },
   setClipPlanes (gl, clipPlanes, renderObject)
   {
      this .numClipPlanes = 0;

      for (const clipPlane of clipPlanes)
      {
         if (clipPlane .isClipPlane)
            clipPlane .setShaderUniforms (gl, this, renderObject);
      }
   },
   setUniforms: (() =>
   {
      const
         normalMatrix      = new Numbers_Matrix3 (),
         normalMatrixArray = new Float32Array (9);

      return function (gl, renderContext, geometryContext, front = true)
      {
         const { renderObject, viewport, modelViewMatrix, fogNode, appearanceNode, hAnimNode, textureNode: geometryTextureNode, localObjects } = renderContext;

         const
            stylePropertiesNode = appearanceNode .getStyleProperties (geometryContext .geometryType),
            materialNode        = front ? appearanceNode .getMaterial () : appearanceNode .getBackMaterial (),
            textureNode         = geometryTextureNode ?? appearanceNode .getTexture (),
            renderCount         = renderObject .getRenderCount ();

         // Set global uniforms.

         if (this .renderCount !== renderCount)
         {
            this .renderCount = renderCount;

            // Set projection matrix.

            gl .uniformMatrix4fv (this .x3d_ProjectionMatrix,  false, renderObject .getProjectionMatrixArray ());
            gl .uniformMatrix4fv (this .x3d_EyeMatrix,         false, renderObject .getEyeMatrixArray ());
            gl .uniformMatrix4fv (this .x3d_ViewMatrix,        false, renderObject .getViewMatrixArray ());
            gl .uniformMatrix4fv (this .x3d_CameraSpaceMatrix, false, renderObject .getCameraSpaceMatrixArray ());

            // Fog

            this .fogNode = null;

            // Set global lights and global texture projectors.

            this .numEnvironmentLights = 0;
            this .numLights            = 0;
            this .numTextureProjectors = 0;

            this .environmentLightNodes .length = 0;
            this .lightNodes            .length = 0;
            this .textureProjectorNodes .length = 0;

            for (const globalLights of renderObject .getGlobalLights ())
               globalLights .setShaderUniforms (gl, this, renderObject);

            this .numGlobalEnvironmentLights = this .numEnvironmentLights;
            this .numGlobalLights            = this .numLights;
            this .numGlobalTextureProjectors = this .numTextureProjectors;

            // Logarithmic depth buffer support

            if (renderObject .getLogarithmicDepthBuffer ())
            {
               const
                  navigationInfoNode = renderObject .getNavigationInfo (),
                  viewpointNode      = renderObject .getViewpoint ();

               gl .uniform1f (this .x3d_LogarithmicFarFactor1_2, 1 / Math .log2 (viewpointNode .getFarDistance (navigationInfoNode) + 1));
            }
         }

         // Set viewport.

         gl .uniform4i (this .x3d_Viewport, ... viewport);

         // Set model view matrix.

         gl .uniformMatrix4fv (this .x3d_ModelViewMatrix, false, modelViewMatrix);

         // Set normal matrix if needed.

         if (this .x3d_NormalMatrix)
         {
            // Set matrix and transpose it.
            normalMatrix [0] = modelViewMatrix [0]; normalMatrix [3] = modelViewMatrix [1]; normalMatrix [6] = modelViewMatrix [ 2];
            normalMatrix [1] = modelViewMatrix [4]; normalMatrix [4] = modelViewMatrix [5]; normalMatrix [7] = modelViewMatrix [ 6];
            normalMatrix [2] = modelViewMatrix [8]; normalMatrix [5] = modelViewMatrix [9]; normalMatrix [8] = modelViewMatrix [10];

            normalMatrixArray .set (normalMatrix .inverse ());

            gl .uniformMatrix3fv (this .x3d_NormalMatrix, false, normalMatrixArray);
         }

         // Fog

         fogNode ?.setShaderUniforms (gl, this);

         // Clip planes and local lights

         this .numClipPlanes        = 0;
         this .numEnvironmentLights = this .numGlobalEnvironmentLights;
         this .numLights            = this .numGlobalLights;
         this .numTextureProjectors = this .numGlobalTextureProjectors;

         for (const localObject of localObjects)
            localObject .setShaderUniforms (gl, this, renderObject);

         // Alpha

         gl .uniform1f (this .x3d_AlphaCutoff, appearanceNode .getAlphaCutoff ());

         // Style Properties

         stylePropertiesNode ?.setShaderUniforms (gl, this);

         // Material

         materialNode .setShaderUniforms (gl, this, appearanceNode .getTextureTransformMapping (), geometryContext .getTextureCoordinateMapping (), front);

         // Texture

         textureNode ?.setShaderUniforms (gl, this .x3d_Texture [0], this);

         appearanceNode  .getTextureTransform ()  .setShaderUniforms (gl, this);
         geometryContext .getTextureCoordinate () .setShaderUniforms (gl, this);

         // Skinning

         hAnimNode ?.setShaderUniforms (gl, this);
      };
   })(),
   enable (gl)
   {
      gl .useProgram (this .getProgram ());

      for (const location of this .textures)
      {
         const
            texture     = location .textureNode,
            textureUnit = this .getBrowser () .popTextureUnit ();

         if (textureUnit === undefined)
         {
            console .warn (`Not enough combined texture units for uniform variable '${location .name}' available.`);
            break;
         }

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (texture .getTarget (), texture .getTexture ());
         gl .uniform1i (location, textureUnit);
      }
   },
   enableUintAttrib (gl, name, buffer, components, stride, offset, divisor = 0)
   {
      const location = gl .getAttribLocation (this .getProgram (), name);

      if (location === -1)
         return;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribIPointer (location, components, gl .UNSIGNED_INT, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableFloatAttrib (gl, name, buffer, components, stride, offset, divisor = 0)
   {
      const location = gl .getAttribLocation (this .getProgram (), name);

      if (location === -1)
         return;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, components, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableMatrix3Attrib (gl, name, buffer, stride, offset)
   {
      const location0 = gl .getAttribLocation (this .getProgram (), name);

      if (location0 === -1)
         return;

      stride ||= 36;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);

      for (let i = 0; i < 3; ++ i)
      {
         const location = location0 + i;

         gl .enableVertexAttribArray (location);
         gl .vertexAttribPointer (location, 3, gl .FLOAT, false, stride, offset + 12 * i);
      }
   },
   enableMatrix4Attrib (gl, name, buffer, stride, offset)
   {
      const location0 = gl .getAttribLocation (this .getProgram (), name);

      if (location0 === -1)
         return;

      stride ||= 64;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);

      for (let i = 0; i < 4; ++ i)
      {
         const location = location0 + i;

         gl .enableVertexAttribArray (location);
         gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset + 16 * i);
      }
   },
   enableCoordIndexAttribute (gl, buffer, stride, offset)
   {
      const location = this .x3d_CoordIndex;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribIPointer (location, 1, gl .UNSIGNED_INT, stride, offset);
   },
   enableLineStippleAttribute (gl, buffer, stride, offset)
   {
      const location = this .x3d_LineStipple;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 3, gl .FLOAT, false, stride, offset);
   },
   enableFogDepthAttribute (gl, buffer, stride, offset)
   {
      const location = this .x3d_FogDepth;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 1, gl .FLOAT, false, stride, offset);
   },
   enableColorAttribute (gl, buffer, stride, offset, divisor = 0)
   {
      const location = this .x3d_Color;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor)
   },
   enableTexCoordAttribute (gl, buffers, stride, offset, divisor = 0)
   {
      for (const [i, location] of this .x3d_TexCoord)
      {
         gl .bindBuffer (gl .ARRAY_BUFFER, buffers [i]);
         gl .enableVertexAttribArray (location);
         gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset);
         gl .vertexAttribDivisor (location, divisor);
      }
   },
   enableNormalAttribute (gl, buffer, stride, offset, divisor = 0)
   {
      const location = this .x3d_Normal;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 3, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableTangentAttribute (gl, buffer, stride, offset, divisor = 0)
   {
      const location = this .x3d_Tangent;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableVertexAttribute (gl, buffer, stride, offset)
   {
      const location = this .x3d_Vertex;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset);
   },
   enableParticleAttribute (gl, buffer, stride, offset, divisor)
   {
      const location = this .x3d_Particle;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableParticleVelocityAttribute (gl, buffer, stride, offset, divisor)
   {
      const location = this .x3d_ParticleVelocity;

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);
      gl .enableVertexAttribArray (location);
      gl .vertexAttribPointer (location, 3, gl .FLOAT, false, stride, offset);
      gl .vertexAttribDivisor (location, divisor);
   },
   enableInstanceMatrixAttribute (gl, buffer, stride, offset, divisor)
   {
      const location0 = this .x3d_InstanceMatrix;

      stride ||= 64; // 4 Bytes * 16

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);

      for (let i = 0; i < 4; ++ i)
      {
         const location = location0 + i;

         gl .enableVertexAttribArray (location);
         gl .vertexAttribPointer (location, 4, gl .FLOAT, false, stride, offset + 16 * i);
         gl .vertexAttribDivisor (location, divisor);
      }
   },
   enableInstanceNormalMatrixAttribute (gl, buffer, stride, offset, divisor)
   {
      const location0 = this .x3d_InstanceNormalMatrix;

      stride ||= 36; // 4 Bytes * 9

      gl .bindBuffer (gl .ARRAY_BUFFER, buffer);

      for (let i = 0; i < 3; ++ i)
      {
         const location = location0 + i;

         gl .enableVertexAttribArray (location);
         gl .vertexAttribPointer (location, 3, gl .FLOAT, false, stride, offset + 12 * i);
         gl .vertexAttribDivisor (location, divisor);
      }
   },
   // getProgramInfo ()
   // {
   //    function cmp (lhs, rhs) { return lhs < rhs ? -1 : lhs > rhs ? 1 : 0; }

   //    const
   //       program = this .getProgram (),
   //       gl      = this .getBrowser () .getContext ();

   //    const
   //       result = {
   //          attributes: [ ],
   //          uniforms: [ ],
   //          attributeCount: 0,
   //          uniformCount: 0,
   //       },
   //       activeUniforms   = gl .getProgramParameter (program, gl .ACTIVE_UNIFORMS),
   //       activeAttributes = gl .getProgramParameter (program, gl .ACTIVE_ATTRIBUTES);

   //    // Taken from the WebGl spec:
   //    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14
   //    const enums = {
   //       0x8B50: 'vec2',
   //       0x8B51: 'vec3',
   //       0x8B52: 'vec4',
   //       0x8B53: 'ivec2',
   //       0x8B54: 'ivec3',
   //       0x8B55: 'ivec4',
   //       0x8B56: 'bool',
   //       0x8B57: 'bvec2',
   //       0x8B58: 'bvec3',
   //       0x8B59: 'bvec4',
   //       0x8B5A: 'mat2',
   //       0x8B5B: 'mat3',
   //       0x8B5C: 'mat4',
   //       0x8B5E: 'sampler2D',
   //       0x8B60: 'samplerCube',
   //       0x1400: 'byte',
   //       0x1401: 'ubyte',
   //       0x1402: 'short',
   //       0x1403: 'ushort',
   //       0x1404: 'int',
   //       0x1405: 'uint',
   //       0x1406: 'float',
   //    };

   //    // Loop through active uniforms
   //    for (let i = 0; i < activeUniforms; ++ i)
   //    {
   //       const uniform = gl .getActiveUniform (program, i);
   //       uniform .typeName = enums [uniform.type];
   //       result .uniforms .push (Object .assign ({ }, uniform));
   //       result .uniformCount += uniform .size;
   //    }

   //    // Loop through active attributes
   //    for (let i = 0; i < activeAttributes; ++ i)
   //    {
   //       const attribute = gl .getActiveAttrib (program, i);
   //       attribute .typeName = enums [attribute .type];
   //       result .attributes .push (Object .assign ({ }, attribute));
   //       result .attributeCount += attribute .size;
   //    }

   //    result .uniforms   .sort ((a, b) => cmp (a .name, b .name));
   //    result .attributes .sort ((a, b) => cmp (a .name, b .name));

   //    return result;
   // },
   // printProgramInfo ()
   // {
   //    const programInfo = this .getProgramInfo ();

   //    console .log (this .getName ());
   //    console .table (programInfo .attributes);
   //    console .log (this .getName (), "attributeCount", programInfo .attributeCount);
   //    console .log (this .getName ());
   //    console .table (programInfo .uniforms);
   //    console .log (this .getName (), "uniformCount", programInfo .uniformCount);
   // },
   dispose ()
   { },
});

Object .defineProperties (X3DProgrammableShaderObject, Core_X3DNode .getStaticProperties ("X3DProgrammableShaderObject", "Shaders", 1));

const X3DProgrammableShaderObject_default_ = X3DProgrammableShaderObject;
;

/* harmony default export */ const Shaders_X3DProgrammableShaderObject = (Namespace/* default */.A .add ("X3DProgrammableShaderObject", X3DProgrammableShaderObject_default_));
;// ./src/x_ite/Components/Networking/X3DNetworkSensorNode.js




function X3DNetworkSensorNode (executionContext)
{
   Core_X3DSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DNetworkSensorNode);
}

Object .setPrototypeOf (X3DNetworkSensorNode .prototype, Core_X3DSensorNode .prototype);

Object .defineProperties (X3DNetworkSensorNode, Core_X3DNode .getStaticProperties ("X3DNetworkSensorNode", "Networking", 1));

const X3DNetworkSensorNode_default_ = X3DNetworkSensorNode;
;

/* harmony default export */ const Networking_X3DNetworkSensorNode = (Namespace/* default */.A .add ("X3DNetworkSensorNode", X3DNetworkSensorNode_default_));
;// ./src/x_ite/Components/Networking/LoadSensor.js








function LoadSensor (executionContext)
{
   Networking_X3DNetworkSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .LoadSensor);

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.3)
      this .addAlias ("watchList", this ._children);

   // Private properties

   this .urlObjects = [ ];
   this .aborted    = false;
   this .timeOutId  = undefined;
}

Object .assign (Object .setPrototypeOf (LoadSensor .prototype, Networking_X3DNetworkSensorNode .prototype),
{
   initialize ()
   {
      Networking_X3DNetworkSensorNode .prototype .initialize .call (this);

      this ._enabled  .addInterest ("set_enabled__",  this);
      this ._timeOut  .addInterest ("set_timeOut__",  this);
      this ._children .addInterest ("set_children__", this);

      this .set_children__ ();
   },
   set_enabled__ ()
   {
      if (this ._enabled .getValue ())
         this .reset ();

      else
      {
         this .abort ();
         this .remove ();
      }
   },
   set_timeOut__ ()
   {
      if (!this ._isActive .getValue ())
         return;

      const timeOut = this ._timeOut .getValue ();

      this .clearTimeout ();

      this .aborted = false;

      if (timeOut > 0)
         this .timeOutId = setTimeout (this .abort .bind (this), timeOut * 1000);
   },
   set_children__ ()
   {
      this .reset ();
   },
   set_loadState__ (urlObject)
   {
      if (urlObject .checkLoadState () === Base_X3DConstants .NOT_STARTED_STATE)
         return;

      this .count ();
   },
   count ()
   {
      const urlObjects = this .urlObjects;

      if (urlObjects .length)
      {
         let
            complete = 0,
            failed   = 0;

         for (const urlObject of urlObjects)
         {
            complete += urlObject .checkLoadState () === Base_X3DConstants .COMPLETE_STATE;
            failed   += urlObject .checkLoadState () === Base_X3DConstants .FAILED_STATE;
         }

         const
            loaded   = complete === urlObjects .length,
            progress = complete / urlObjects .length;

         if (this .aborted || failed || loaded)
         {
            this .clearTimeout ();

            this ._isActive = false;
            this ._isLoaded = loaded;
            this ._progress = progress;

            if (loaded)
               this ._loadTime = this .getBrowser () .getCurrentTime ();
         }
         else
         {
            if (this ._isActive .getValue ())
            {
               this ._progress = progress;
            }
            else
            {
               this ._isActive = true;
               this ._progress = progress;

               this .set_timeOut__ ();
            }
         }
      }
      else
      {
         this ._isActive = false;
         this ._isLoaded = false;
         this ._progress = 0;
      }
   },
   abort ()
   {
      this .clearTimeout ();

      this .aborted = true;

      if (this ._enabled .getValue ())
         this .count ();
   },
   reset ()
   {
      this .remove ();

      if (!this ._enabled .getValue ())
         return;

      const urlObjects = this .urlObjects;

      for (const node of this ._children)
      {
         const urlObject = Base_X3DCast (Base_X3DConstants .X3DUrlObject, node);

         if (urlObject)
            urlObjects .push (urlObject);
      }

      for (const urlObject of urlObjects)
         urlObject ._loadState .addInterest ("set_loadState__", this, urlObject);

      this .count ();
   },
   remove ()
   {
      this .clearTimeout ();

      const urlObjects = this .urlObjects;

      for (const urlObject of urlObjects)
         urlObject ._loadState .removeInterest ("set_loadState__", this);

      urlObjects .length = 0;
   },
   clearTimeout ()
   {
      clearTimeout (this .timeOutId);

      this .timeOutId = undefined;
   },
});

Object .defineProperties (LoadSensor,
{
   ... Core_X3DNode .getStaticProperties ("LoadSensor", "Networking", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "timeOut",     new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isLoaded",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "progress",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "loadTime",    new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",    new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const LoadSensor_default_ = LoadSensor;
;

/* harmony default export */ const Networking_LoadSensor = (Namespace/* default */.A .add ("LoadSensor", LoadSensor_default_));
;// ./src/x_ite/Components/Shaders/ComposedShader.js










function ComposedShader (executionContext)
{
   Shaders_X3DShaderNode               .call (this, executionContext);
   Shaders_X3DProgrammableShaderObject .call (this, executionContext);

   this .addType (Base_X3DConstants .ComposedShader);

   this .loadSensor                = new Networking_LoadSensor (executionContext);
   this .transformFeedbackVaryings = [ ];
}

Object .assign (Object .setPrototypeOf (ComposedShader .prototype, Shaders_X3DShaderNode .prototype),
   Shaders_X3DProgrammableShaderObject .prototype,
{
   initialize ()
   {
      Shaders_X3DShaderNode               .prototype .initialize .call (this);
      Shaders_X3DProgrammableShaderObject .prototype .initialize .call (this);

      // https://www.web3d.org/documents/specifications/19775-1/V4.0/Part01/shaders_glsl.html#relinkingprograms
      this ._activate .addInterest ("set_activate__", this);
      this ._language .addInterest ("set_loaded__",   this);

      this ._parts .addFieldInterest (this .loadSensor ._children);

      this .loadSensor ._isLoaded .addInterest ("connectLoaded", this);
      this .loadSensor ._children = this ._parts;
      this .loadSensor .setPrivate (true);
      this .loadSensor .setup ();

      if (this .loadSensor ._isLoaded .getValue ())
         this .set_loaded__ ();
      else
         this .connectLoaded ();
   },
   connectLoaded ()
   {
      this .loadSensor ._isLoaded .removeInterest ("connectLoaded", this);
      this .loadSensor ._isLoaded .addInterest ("set_loaded__", this);
   },
   addUserDefinedField (accessType, name, field)
   {
      const shaderFields = this .isInitialized () && this .isValid ();

      if (shaderFields)
         this .removeShaderFields ();

      Shaders_X3DShaderNode .prototype .addUserDefinedField .call (this, accessType, name, field);

      if (shaderFields)
         this .addShaderFields ();
   },
   removeUserDefinedField (name)
   {
      const shaderFields = this .isInitialized () && this .isValid ();

      if (shaderFields)
         this .removeShaderFields ();

      Shaders_X3DShaderNode .prototype .removeUserDefinedField .call (this, name);

      if (shaderFields)
         this .addShaderFields ();
   },
   setTransformFeedbackVaryings (value)
   {
      this .transformFeedbackVaryings = value;
   },
   getProgram ()
   {
      return this .program;
   },
   set_activate__ ()
   {
      if (!this ._activate .getValue ())
         return;

      this .set_loaded__ ();
   },
   set_loaded__ ()
   {
      if (this .loadSensor ._isLoaded .getValue () && this ._language .getValue () === "GLSL")
      {
         const
            gl      = this .getBrowser () .getContext (),
            program = gl .createProgram ();

         if (this .isValid ())
            this .removeShaderFields ();

         gl .deleteProgram (this .program);

         this .program = program;

         for (const node of this ._parts)
         {
            const partNode = Base_X3DCast (Base_X3DConstants .ShaderPart, node);

            if (partNode ?.getShader ())
               gl .attachShader (program, partNode .getShader ());
         }

         if (this .transformFeedbackVaryings .length)
            gl .transformFeedbackVaryings (program, this .transformFeedbackVaryings, gl .INTERLEAVED_ATTRIBS);

         gl .linkProgram (program);

         if (gl .getProgramParameter (program, gl .LINK_STATUS))
         {
            this .setValid (true);
            this .getDefaultUniformsAndAttributes ();
            this .addShaderFields ();
         }
         else
         {
            this .setValid (false);

            if (this ._parts .length)
            {
               console .warn (`Couldn't initialize ${this .getTypeName ()} '${this .getName ()}': ${gl .getProgramInfoLog (program)}`);
            }
         }
      }
      else
      {
         this .setValid (false);
      }
   },
   dispose ()
   {
      Shaders_X3DProgrammableShaderObject .prototype .dispose .call (this);
      Shaders_X3DShaderNode               .prototype .dispose .call (this);
   },
});

Object .defineProperties (ComposedShader,
{
   ... Core_X3DNode .getStaticProperties ("ComposedShader", "Shaders", 1, "shaders", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "activate",   new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isSelected", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isValid",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "language",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "parts",      new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ComposedShader_default_ = ComposedShader;
;

/* harmony default export */ const Shaders_ComposedShader = (Namespace/* default */.A .add ("ComposedShader", ComposedShader_default_));
;// ./src/x_ite/Browser/Texturing/ModeType.js
let ModeType_i = 0;

const ModeType =
{
   REPLACE:                   ModeType_i ++,
   MODULATE:                  ModeType_i ++,
   MODULATE2X:                ModeType_i ++,
   MODULATE4X:                ModeType_i ++,
   ADD:                       ModeType_i ++,
   ADDSIGNED:                 ModeType_i ++,
   ADDSIGNED2X:               ModeType_i ++,
   ADDSMOOTH:                 ModeType_i ++,
   SUBTRACT:                  ModeType_i ++,
   BLENDDIFFUSEALPHA:         ModeType_i ++,
   BLENDTEXTUREALPHA:         ModeType_i ++,
   BLENDFACTORALPHA:          ModeType_i ++,
   BLENDCURRENTALPHA:         ModeType_i ++,
   MODULATEALPHA_ADDCOLOR:    ModeType_i ++,
   MODULATEINVALPHA_ADDCOLOR: ModeType_i ++,
   MODULATEINVCOLOR_ADDALPHA: ModeType_i ++,
   DOTPRODUCT3:               ModeType_i ++,
   SELECTARG1:                ModeType_i ++,
   SELECTARG2:                ModeType_i ++,
   OFF:                       ModeType_i ++,
};

const ModeType_default_ = ModeType;
;

/* harmony default export */ const Texturing_ModeType = (Namespace/* default */.A .add ("ModeType", ModeType_default_));
;// ./src/x_ite/Browser/Texturing/SourceType.js
let SourceType_i = 0;

const SourceType =
{
   DEFAULT:  SourceType_i ++,
   DIFFUSE:  SourceType_i ++,
   SPECULAR: SourceType_i ++,
   FACTOR:   SourceType_i ++,
};

const SourceType_default_ = SourceType;
;

/* harmony default export */ const Texturing_SourceType = (Namespace/* default */.A .add ("SourceType", SourceType_default_));
;// ./src/x_ite/Browser/Texturing/FunctionType.js
let FunctionType_i = 0;

const FunctionType =
{
   DEFAULT:        FunctionType_i ++,
   COMPLEMENT:     FunctionType_i ++,
   ALPHAREPLICATE: FunctionType_i ++,
};

const FunctionType_default_ = FunctionType;
;

/* harmony default export */ const Texturing_FunctionType = (Namespace/* default */.A .add ("FunctionType", FunctionType_default_));
;// ./src/x_ite/Browser/Texturing/TextureCoordinateGeneratorModeType.js
let TextureCoordinateGeneratorModeType_i = 0;

const TextureCoordinateGeneratorModeType_ModeType =
{
   NONE:                        TextureCoordinateGeneratorModeType_i ++,
   SPHERE:                      TextureCoordinateGeneratorModeType_i ++,
   CAMERASPACENORMAL:           TextureCoordinateGeneratorModeType_i ++,
   CAMERASPACEPOSITION:         TextureCoordinateGeneratorModeType_i ++,
   CAMERASPACEREFLECTIONVECTOR: TextureCoordinateGeneratorModeType_i ++,
   SPHERE_LOCAL:                TextureCoordinateGeneratorModeType_i ++,
   COORD:                       TextureCoordinateGeneratorModeType_i ++,
   COORD_EYE:                   TextureCoordinateGeneratorModeType_i ++,
   NOISE:                       TextureCoordinateGeneratorModeType_i ++,
   NOISE_EYE:                   TextureCoordinateGeneratorModeType_i ++,
   SPHERE_REFLECT:              TextureCoordinateGeneratorModeType_i ++,
   SPHERE_REFLECT_LOCAL:        TextureCoordinateGeneratorModeType_i ++,
};

const TextureCoordinateGeneratorModeType_default_ = TextureCoordinateGeneratorModeType_ModeType;
;

/* harmony default export */ const TextureCoordinateGeneratorModeType = (Namespace/* default */.A .add ("TextureCoordinateGeneratorModeType", TextureCoordinateGeneratorModeType_default_));
;// ./src/assets/shaders/Types.glsl.js








const Types_glsl_default_ = () => /* glsl */ `
#define X_ITE
#define x3d_None 0
#define x3d_Points 0
#define x3d_Lines 1
#define x3d_Geometry2D 2
#define x3d_Geometry3D 3
#define x3d_MaxClipPlanes ${(/* inlined export .maxClipPlanes */6)}
#define x3d_LinearFog 1
#define x3d_ExponentialFog 2
#define x3d_Exponential2Fog 3
#define x3d_DirectionalLight 1
#define x3d_PointLight 2
#define x3d_SpotLight 3
#define x3d_MaxTextures ${(/* inlined export .maxTextures */4)}
#define x3d_TextureType2D 2
#define x3d_TextureType3D 3
#define x3d_TextureTypeCube 4
#define x3d_Replace ${Texturing_ModeType .REPLACE}
#define x3d_Modulate ${Texturing_ModeType .MODULATE}
#define x3d_Modulate2X ${Texturing_ModeType .MODULATE2X}
#define x3d_Modulate4X ${Texturing_ModeType .MODULATE4X}
#define x3d_Add ${Texturing_ModeType .ADD}
#define x3d_AddSigned ${Texturing_ModeType .ADDSIGNED}
#define x3d_AddSigned2X ${Texturing_ModeType .ADDSIGNED2X}
#define x3d_AddSmooth ${Texturing_ModeType .ADDSMOOTH}
#define x3d_Subtract ${Texturing_ModeType .SUBTRACT}
#define x3d_BlendDiffuseAlpha ${Texturing_ModeType .BLENDDIFFUSEALPHA}
#define x3d_BlendTextureAlpha ${Texturing_ModeType .BLENDTEXTUREALPHA}
#define x3d_BlendFactorAlpha ${Texturing_ModeType .BLENDFACTORALPHA}
#define x3d_BlendCurrentAlpha ${Texturing_ModeType .BLENDCURRENTALPHA}
#define x3d_ModulateAlphaAddColor ${Texturing_ModeType .MODULATEALPHA_ADDCOLOR}
#define x3d_ModulateInvAlphaAddColor ${Texturing_ModeType .MODULATEINVALPHA_ADDCOLOR}
#define x3d_ModulateInvColorAddAlpha ${Texturing_ModeType .MODULATEINVCOLOR_ADDALPHA}
#define x3d_DotProduct3 ${Texturing_ModeType .DOTPRODUCT3}
#define x3d_SelectArg1 ${Texturing_ModeType .SELECTARG1}
#define x3d_SelectArg2 ${Texturing_ModeType .SELECTARG2}
#define x3d_Off ${Texturing_ModeType .OFF}
#define x3d_Diffuse ${Texturing_SourceType .DIFFUSE}
#define x3d_Specular ${Texturing_SourceType .SPECULAR}
#define x3d_Factor ${Texturing_SourceType .FACTOR}
#define x3d_Complement ${Texturing_FunctionType .COMPLEMENT}
#define x3d_AlphaReplicate ${Texturing_FunctionType .ALPHAREPLICATE}
#define x3d_Sphere ${TextureCoordinateGeneratorModeType .SPHERE}
#define x3d_CameraSpaceNormal ${TextureCoordinateGeneratorModeType .CAMERASPACENORMAL}
#define x3d_CameraSpacePosition ${TextureCoordinateGeneratorModeType .CAMERASPACEPOSITION}
#define x3d_CameraSpaceReflectionVector ${TextureCoordinateGeneratorModeType .CAMERASPACEREFLECTIONVECTOR}
#define x3d_SphereLocal ${TextureCoordinateGeneratorModeType .SPHERE_LOCAL}
#define x3d_Coord ${TextureCoordinateGeneratorModeType .COORD}
#define x3d_CoordEye ${TextureCoordinateGeneratorModeType .COORD_EYE}
#define x3d_Noise ${TextureCoordinateGeneratorModeType .NOISE}
#define x3d_NoiseEye ${TextureCoordinateGeneratorModeType .NOISE_EYE}
#define x3d_SphereReflect ${TextureCoordinateGeneratorModeType .SPHERE_REFLECT}
#define x3d_SphereReflectLocal ${TextureCoordinateGeneratorModeType .SPHERE_REFLECT_LOCAL}
#define x3d_GeometryPoints 0
#define x3d_GeometryLines 1
#define x3d_NoneClipPlane vec4(88.,51.,68.,33.)
#define x3d_NoneFog 0
#define x3d_NoneLight 0
#define x3d_NoneTexture 0
#if defined(X3D_FOG)
struct x3d_FogParameters{mediump int type;mediump vec3 color;mediump float visibilityStart;mediump float visibilityRange;mediump mat3 matrix;};
#endif
#if defined(X3D_LIGHTING)
struct x3d_LightSourceParameters{mediump int type;mediump vec3 color;mediump float intensity;mediump float ambientIntensity;mediump vec3 attenuation;mediump vec3 location;mediump vec3 direction;mediump float radius;mediump float beamWidth;mediump float cutOffAngle;mediump mat3 matrix;
#if defined(X3D_SHADOWS)
mediump vec3 shadowColor;mediump float shadowIntensity;mediump float shadowBias;mediump mat4 shadowMatrix;mediump int shadowMapSize;
#endif
};
#endif
#if defined(X3D_USE_IBL)
struct x3d_EnvironmentLightSourceParameters{mediump vec3 color;mediump float intensity;mediump float ambientIntensity;mediump mat3 rotation;mediump samplerCube diffuseTexture;bool diffuseTextureLinear;mediump int diffuseTextureLevels;mediump samplerCube specularTexture;bool specularTextureLinear;mediump int specularTextureLevels;mediump sampler2D ggxLUTTexture;
#if defined(X3D_SHEEN_MATERIAL_EXT)
mediump samplerCube sheenTexture;bool sheenTextureLinear;mediump int sheenTextureLevels;mediump sampler2D charlieLUTTexture;
#endif
};
#endif
#if defined(X3D_GEOMETRY_0D)&&defined(X3D_STYLE_PROPERTIES)
struct x3d_PointPropertiesParameters{mediump float pointSizeScaleFactor;mediump float pointSizeMinValue;mediump float pointSizeMaxValue;mediump vec3 attenuation;};
#endif
#if defined(X3D_GEOMETRY_1D)&&defined(X3D_STYLE_PROPERTIES)
struct x3d_LinePropertiesParameters{mediump int linetype;mediump float lineStippleScale;
#if defined(X3D_STYLE_PROPERTIES_TEXTURE)
sampler2D texture;
#endif
};
#endif
#if(defined(X3D_GEOMETRY_2D)||defined(X3D_GEOMETRY_3D))&&defined(X3D_STYLE_PROPERTIES)
struct x3d_FillPropertiesParameters{bool filled;bool hatched;mediump vec3 hatchColor;mediump float scale;
#if defined(X3D_STYLE_PROPERTIES_TEXTURE)
sampler2D texture;
#endif
};
#endif
#if defined(X3D_UNLIT_MATERIAL)
struct x3d_UnlitMaterialParameters{mediump vec3 emissiveColor;mediump float normalScale;mediump float transparency;};
#endif
#if defined(X3D_GOURAUD_MATERIAL)||defined(X3D_PHONG_MATERIAL)
struct x3d_MaterialParameters{mediump float ambientIntensity;mediump vec3 diffuseColor;mediump vec3 specularColor;mediump vec3 emissiveColor;mediump float shininess;mediump float occlusionStrength;mediump float normalScale;mediump float transparency;};
#endif
#if defined(X3D_PHYSICAL_MATERIAL)
struct x3d_PhysicalMaterialParameters{
#if defined(X3D_MATERIAL_SPECULAR_GLOSSINESS)
mediump vec3 diffuseColor;mediump vec3 specularColor;mediump float glossiness;
#elif defined(X3D_MATERIAL_METALLIC_ROUGHNESS)
mediump vec3 baseColor;mediump float metallic;mediump float roughness;
#endif
mediump vec3 emissiveColor;mediump float occlusionStrength;mediump float normalScale;mediump float transparency;};
#endif
${MaterialTextures .structs ()}
#if defined(X3D_MULTI_TEXTURING)
struct x3d_MultiTextureParameters{mediump int mode;mediump int alphaMode;mediump int source;mediump int function;};
#endif
#if defined(X3D_TEXTURE)||defined(X3D_MATERIAL_TEXTURES)
struct x3d_TextureCoordinateGeneratorParameters{mediump int mode;mediump float parameter[6];};
#endif
#if defined(X3D_TEXTURE_PROJECTION)
struct x3d_TextureProjectorParameters{mediump vec3 color;mediump float intensity;mediump vec3 location;mediump vec2 params;mediump mat4 matrix;};
#endif
`
;

/* harmony default export */ const Types_glsl = (Namespace/* default */.A .add ("Types.glsl", Types_glsl_default_));
;// ./src/x_ite/Browser/Shaders/ShaderSource.js


const ShaderSource =
{
   getSource (gl, browser, source, options)
   {
      const
         COMMENTS     = "\\s+|/\\*[\\s\\S]*?\\*/|//.*?\\n",
         LINE         = "#line\\s+.*?\\n",
         IF           = "#if\\s+.*?\\n",
         ELIF         = "#elif\\s+.*?\\n",
         IFDEF        = "#ifdef\\s+.*?\\n",
         IFNDEF       = "#ifndef\\s+.*?\\n",
         ELSE         = "#else.*?\\n",
         ENDIF        = "#endif.*?\\n",
         DEFINE       = "#define\\s+(?:[^\\n\\\\]|\\\\[^\\r\\n]|\\\\\\r?\\n)*\\n",
         UNDEF        = "#undef\\s+.*?\\n",
         PRAGMA       = "#pragma\\s+.*?\\n",
         PREPROCESSOR =  `${LINE}|${IF}|${ELIF}|${IFDEF}|${IFNDEF}|${ELSE}|${ENDIF}|${DEFINE}|${UNDEF}|${PRAGMA}`,
         VERSION      = "#version\\s+.*?\\n",
         EXTENSION    = "#extension\\s+.*?\\n";

      const
         GLSL  = new RegExp (`^((?:${COMMENTS}|${PREPROCESSOR})*(?:${VERSION})?(?:${COMMENTS}|${PREPROCESSOR}|${EXTENSION})*)`, "s"),
         match = source .match (GLSL);

      // const
      //    COMMENTS = "\\s+|/\\*.*?\\*/|//.*?\\n",
      //    VERSION  = "#version\\s+.*?\\n",
      //    ANY      = ".*";

      // const
      //    GLSL  = new RegExp ("^((?:" + COMMENTS + ")?(?:" + VERSION + ")?)(" + ANY + ")$", "s"),
      //    match = source .match (GLSL);

      if (!match)
         return source;

      // Constants

      const defines = /* glsl */ `
${options .map (option => `#define ${option}`) .join ("\n")}
#define x3d_MaxLights ${browser .getMaxLights ()}
`

      // Adjust precision of struct types;

      const
         matchFloat     = source .match (/\s*precision\s+(lowp|mediump|highp)\s+float\s*;/),
         matchInt       = source .match (/\s*precision\s+(lowp|mediump|highp)\s+int\s*;/),
         precisionFloat = matchFloat ?.[1] ?? "mediump",
         precisionInt   = matchInt   ?.[1] ?? "mediump";

      const types = Types_glsl ()
         .replace (/mediump\s+(float|vec2|vec3|mat3|mat4)/g, `${precisionFloat} \$1`)
         .replace (/mediump\s+(int)/g,                       `${precisionInt} \$1`);

      const lines = (match [1] .match (/\n/g) ?.length ?? 0) + 1;

      return `${match [1]}${defines}${types}#line ${lines + 1} -1\n${source .substring (match [0] .length)}`;
   },
};

// function depreciatedWarning (source, deprecated, current)
// {
//    if (source .indexOf (deprecated) === -1)
//       return;

//    console .warn ("Use of '" + deprecated + "' is deprecated, use '" + current + "' instead. See https://create3000.github.io/x_ite/custom-shaders.");
// }

const ShaderSource_default_ = ShaderSource;
;

/* harmony default export */ const Shaders_ShaderSource = (Namespace/* default */.A .add ("ShaderSource", ShaderSource_default_));
;// ./src/x_ite/Browser/Shaders/ShaderCompiler.js


const include = /^\s*#include\s*<([^\/]+)>\s*$/;

function ShaderCompiler (gl)
{
   this .includes          = Shaders_ShaderRegistry .includes [gl .getVersion ()];
   this .sourceFileNumbers = { };

   for (const [i, name] of Object .getOwnPropertyNames (this .includes) .entries ())
      this .sourceFileNumbers [name] = i + 1;
}

Object .assign (ShaderCompiler .prototype,
{
   getSourceFileName (sourceFileNumber)
   {
      return Object .getOwnPropertyNames (this .includes) [sourceFileNumber - 1];
   },
   process (source, parent = 0)
   {
      const lines = source .split ("\n");

      source = "";

      for (const [i, line] of lines .entries ())
      {
         const match = line .match (include);

         if (match)
         {
            source += `#line 1 ${this .sourceFileNumbers [match [1]] ?? 0}\n`;
            source += this .process (this .includes [match [1]] ?.() ?? "", this .sourceFileNumbers [match [1]] ?? 0);
            source += "\n";
            source += `#line ${i + 2} ${parent}\n`;
         }
         else
         {
            source += line;
            source += "\n";
         }
      }

      return source;
   },
});

const ShaderCompiler_default_ = ShaderCompiler;
;

/* harmony default export */ const Shaders_ShaderCompiler = (Namespace/* default */.A .add ("ShaderCompiler", ShaderCompiler_default_));
;// ./src/x_ite/Components/Shaders/ShaderPart.js
/* provided dependency */ var ShaderPart_$ = __webpack_require__(44).A;










const customOptions = [
   "X3D_GEOMETRY_0D",
   "X3D_GEOMETRY_1D",
   "X3D_GEOMETRY_2D",
   "X3D_GEOMETRY_3D",
   "X3D_FOG",
   "X3D_STYLE_PROPERTIES",
   "X3D_UNLIT_MATERIAL",
   "X3D_PHONG_MATERIAL",
   "X3D_LIGHTING",
   "X3D_TEXTURE",
   "X3D_MULTI_TEXTURING",
];

function ShaderPart (executionContext)
{
   Core_X3DNode      .call (this, executionContext);
   Networking_X3DUrlObject .call (this, executionContext);

   this .addType (Base_X3DConstants .ShaderPart);
}

Object .assign (Object .setPrototypeOf (ShaderPart .prototype, Core_X3DNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Core_X3DNode      .prototype .initialize .call (this);
      Networking_X3DUrlObject .prototype .initialize .call (this);

      this .options ??= customOptions .slice ();

      this ._type .addInterest ("set_type__", this);

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_type__ ()
   {
      this .setLoadState (Base_X3DConstants .NOT_STARTED_STATE);

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   getSourceText ()
   {
      return this ._url;
   },
   getOptions ()
   {
      return this .options;
   },
   setOptions (value)
   {
      this .options = value;
   },
   getShader ()
   {
      return this .shader;
   },
   getShaderType: (() =>
   {
      const shaderTypes = new Map ([
         ["VERTEX",          "VERTEX_SHADER"],
         ["TESS_CONTROL",    "TESS_CONTROL_SHADER"],
         ["TESS_EVALUATION", "TESS_EVALUATION_SHADER"],
         ["GEOMETRY",        "GEOMETRY_SHADER"],
         ["FRAGMENT",        "FRAGMENT_SHADER"],
         ["COMPUTE",         "COMPUTE_SHADER"],
      ]);

      return function ()
      {
         return shaderTypes .get (this ._type .getValue ()) ?? "VERTEX_SHADER";
      };
   })(),
   unloadData ()
   {
      this .valid = false;
   },
   loadData ()
   {
      new InputOutput_FileLoader (this) .loadDocument (this ._url, (data, fileURL) =>
      {
         if (data === null)
         {
            // No URL could be loaded.
            this .setLoadState (Base_X3DConstants .FAILED_STATE);
         }
         else
         {
            const
               browser        = this .getBrowser (),
               gl             = browser .getContext (),
               type           = this .getShaderType (),
               options        = ["X3D_" + type] .concat (this .options),
               shaderCompiler = new Shaders_ShaderCompiler (gl),
               source         = Shaders_ShaderSource .getSource (gl, browser, shaderCompiler .process (ShaderPart_$.decodeText (data)), options),
               shader         = gl .createShader (gl [type]);

            gl .deleteShader (this .shader);

            this .shader = shader;

            gl .shaderSource (shader, source);
            gl .compileShader (shader);

            if (!gl .getShaderParameter (shader, gl .COMPILE_STATUS))
            {
               const
                  typeName = this .getTypeName (),
                  name     = this .getName (),
                  log      = gl .getShaderInfoLog (shader),
                  match    = log ?.match (/(\d+):(\d+)/);

               if (match)
               {
                  const fileName = shaderCompiler .getSourceFileName (match [1])
                     || fileURL
                     || this .getExecutionContext () .getWorldURL ();

                  throw new Error (`Error in ${typeName} '${name}' in URL '${fileName}', line ${match [2]}, ${log}`);
               }
               else
               {
                  const fileName = fileURL || this .getExecutionContext () .getWorldURL ();

                  throw new Error (`Error in ${typeName} '${name}' in URL '${fileName}', ${log}`);
               }
            }

            this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
         }
      });
   },
   dispose ()
   {
      Networking_X3DUrlObject .prototype .dispose .call (this);
      Core_X3DNode      .prototype .dispose .call (this);
   },
});

Object .defineProperties (ShaderPart,
{
   ... Core_X3DNode .getStaticProperties ("ShaderPart", "Shaders", 1, "parts", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "type",                 new x_ite_Fields .SFString ("VERTEX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
      ]),
      enumerable: true,
   },
});

const ShaderPart_default_ = ShaderPart;
;

/* harmony default export */ const Shaders_ShaderPart = (Namespace/* default */.A .add ("ShaderPart", ShaderPart_default_));
;// ./src/x_ite/Browser/Shaders/X3DShadersContext.js






const
   _primitiveModes = Symbol (),
   _shaderNodes    = Symbol (),
   _wireframe      = Symbol ();

function X3DShadersContext ()
{
   this [_primitiveModes] = new Map ();
   this [_shaderNodes]    = new Map ();
}

Object .assign (X3DShadersContext .prototype,
{
   initialize ()
   {
      this .setShading (this .getBrowserOptions () .getShading ());
   },
   getShadingLanguageVersion ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .SHADING_LANGUAGE_VERSION);
   },
   getMaxVertexUniformVectors ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_VERTEX_UNIFORM_VECTORS);
   },
   getMaxFragmentUniformVectors ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_FRAGMENT_UNIFORM_VECTORS);
   },
   getMaxVertexAttribs ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_VERTEX_ATTRIBS);
   },
   getMaxVaryingVectors ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_VARYING_VECTORS);
   },
   getPrimitiveMode (primitiveMode)
   {
      return this [_primitiveModes] .get (primitiveMode);
   },
   getShaders ()
   {
      return this [_shaderNodes];
   },
   setShading (type)
   {
      const
         gl             = this .getContext (),
         primitiveModes = this [_primitiveModes];

      if (this [_wireframe])
      {
         this [_wireframe] = false;

         const ext = gl .getExtension ("WEBGL_polygon_mode");

         ext ?.polygonModeWEBGL (gl .FRONT_AND_BACK, ext .FILL_WEBGL);
      }

      switch (type)
      {
         case Core_Shading .POINT:
         {
            primitiveModes
               .set (gl .POINTS,    gl .POINTS)
               .set (gl .LINES,     gl .POINTS)
               .set (gl .TRIANGLES, gl .POINTS);

            break;
         }
         case Core_Shading .WIREFRAME:
         {
            this [_wireframe] = true;

            const ext = gl .getExtension ("WEBGL_polygon_mode");

            ext ?.polygonModeWEBGL (gl .FRONT_AND_BACK, ext .LINE_WEBGL);

            primitiveModes
               .set (gl .POINTS,    gl .POINTS)
               .set (gl .LINES,     gl .LINES)
               .set (gl .TRIANGLES, gl .TRIANGLES);

            break;
         }
         default:
         {
            // case Shading .FLAT:
            // case Shading .GOURAUD:
            // case Shading .PHONG:

            primitiveModes
               .set (gl .POINTS,    gl .POINTS)
               .set (gl .LINES,     gl .LINES)
               .set (gl .TRIANGLES, gl .TRIANGLES);

            break;
         }
      }
   },
   getWireframe ()
   {
      return this [_wireframe];
   },
   createShader ({ name, vertexShader: vs, fragmentShader: fs, options = [ ], attributes= [ ], uniforms = [ ], transformFeedbackVaryings = [ ]})
   {
      if (DEVELOPMENT)
         console .info (`Initializing ${name}Shader.`);

      const
         gl      = this .getContext (),
         version = gl .getVersion ();

      const vertexShader = new Shaders_ShaderPart (this .getPrivateScene ());
      vertexShader ._url .push (encodeURI (vs .startsWith ("data:") ? vs : `data:x-shader/x-vertex,${Shaders_ShaderRegistry .vertex [version] [vs] ()}`));

      vertexShader .setName (`${name}VertexShader`);
      vertexShader .setOptions (options);
      vertexShader .setup ();

      const fragmentShader = new Shaders_ShaderPart (this .getPrivateScene ());
      fragmentShader ._type = "FRAGMENT";
      fragmentShader ._url .push (encodeURI (fs .startsWith ("data:") ? fs : `data:x-shader/x-fragment,${Shaders_ShaderRegistry .fragment [version] [fs] ()}`));

      fragmentShader .setName (`${name}FragmentShader`);
      fragmentShader .setOptions (options);
      fragmentShader .setup ();

      const shaderNode = new Shaders_ComposedShader (this .getPrivateScene ());
      shaderNode ._language = "GLSL";
      shaderNode ._parts .push (vertexShader);
      shaderNode ._parts .push (fragmentShader);

      shaderNode .setName (`${name}Shader`);
      shaderNode .setAttributeNames (attributes);
      shaderNode .setUniformNames (uniforms);
      shaderNode .setTransformFeedbackVaryings (transformFeedbackVaryings);
      shaderNode .setup ();

      return shaderNode;
   },
});

const X3DShadersContext_default_ = X3DShadersContext;
;

/* harmony default export */ const Shaders_X3DShadersContext = (Namespace/* default */.A .add ("X3DShadersContext", X3DShadersContext_default_));
;// ./src/x_ite/Components/Shape/X3DAppearanceNode.js




function X3DAppearanceNode (executionContext)
{
   Core_X3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DAppearanceNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent",   new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "transmission",  new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "volumeScatter", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (X3DAppearanceNode .prototype, Core_X3DNode .prototype),
{
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
   setTransmission (value)
   {
      if (!!value !== this ._transmission .getValue ())
         this ._transmission = value;
   },
   isTransmission ()
   {
      return this ._transmission .getValue ();
   },
   setVolumeScatter (value)
   {
      if (!!value !== this ._volumeScatter .getValue ())
         this ._volumeScatter = value;
   },
   isVolumeScatter ()
   {
      return this ._volumeScatter .getValue ();
   },
});

Object .defineProperties (X3DAppearanceNode, Core_X3DNode .getStaticProperties ("X3DAppearanceNode", "Shape", 1));

const X3DAppearanceNode_default_ = X3DAppearanceNode;
;

/* harmony default export */ const Shape_X3DAppearanceNode = (Namespace/* default */.A .add ("X3DAppearanceNode", X3DAppearanceNode_default_));
;// ./src/x_ite/Components/Shape/Appearance.js










function Appearance (executionContext)
{
   Shape_X3DAppearanceNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Appearance);

   // Private properties

   this .stylePropertiesNode     = [ ];
   this .textureTransformMapping = new Map ();
   this .textureBits             = new Utility_BitSet ();
   this .renderedTextures        = new Set ();
   this .shaderNodes             = [ ];
   this .renderModeNodes         = [ ];
}

Object .assign (Object .setPrototypeOf (Appearance .prototype, Shape_X3DAppearanceNode .prototype),
{
   initialize ()
   {
      Shape_X3DAppearanceNode .prototype .initialize .call (this);

      const browser = this .getBrowser ();

      browser .getRenderingProperties () ._ContentScale .addInterest ("set_contentScale__", this);

      this ._alphaMode        .addInterest ("set_alphaMode__",        this);
      this ._alphaCutoff      .addInterest ("set_alphaCutoff__",      this);
      this ._pointProperties  .addInterest ("set_pointProperties__",  this);
      this ._lineProperties   .addInterest ("set_lineProperties__",   this);
      this ._fillProperties   .addInterest ("set_fillProperties__",   this);
      this ._material         .addInterest ("set_material__",         this);
      this ._backMaterial     .addInterest ("set_backMaterial__",     this);
      this ._texture          .addInterest ("set_texture__",          this);
      this ._textureTransform .addInterest ("set_textureTransform__", this);
      this ._shaders          .addInterest ("set_shaders__",          this);
      this ._blendMode        .addInterest ("set_renderModes__",      this);
      this ._depthMode        .addInterest ("set_renderModes__",      this);

      this ._alphaMode      .addInterest ("set_transparent__", this);
      this ._fillProperties .addInterest ("set_transparent__", this);
      this ._material       .addInterest ("set_transparent__", this);
      this ._texture        .addInterest ("set_transparent__", this);
      this ._blendMode      .addInterest ("set_transparent__", this);

      this ._material     .addInterest ("set_renderedTextures__", this);
      this ._backMaterial .addInterest ("set_renderedTextures__", this);
      this ._texture      .addInterest ("set_renderedTextures__", this);
      this ._shaders      .addInterest ("set_renderedTextures__", this);

      this .set_alphaMode__ ();
      this .set_pointProperties__ ();
      this .set_lineProperties__ ();
      this .set_fillProperties__ ();
      this .set_material__ ();
      this .set_backMaterial__ ();
      this .set_texture__ ();
      this .set_textureTransform__ ();
      this .set_shaders__ ();
      this .set_renderModes__ ();
      this .set_transparent__ ();
      this .set_renderedTextures__ ();
   },
   getAlphaMode ()
   {
      return this .alphaMode;
   },
   getAlphaCutoff ()
   {
      return this .alphaCutoff;
   },
   getStyleProperties (geometryType)
   {
      return this .stylePropertiesNode [geometryType];
   },
   getMaterial ()
   {
      return this .materialNode;
   },
   getBackMaterial ()
   {
      return this .backMaterialNode;
   },
   getTexture ()
   {
      return this .textureNode;
   },
   getTextureBits ()
   {
      return this .textureBits;
   },
   updateTextureBits ()
   {
      this .textureBits .clear ();
      this .textureNode .updateTextureBits (this .textureBits);
   },
   getTextureTransform ()
   {
      return this .textureTransformNode;
   },
   getTextureTransformMapping ()
   {
      return this .textureTransformMapping;
   },
   getShader (geometryContext, renderContext)
   {
      return this .materialNode .getShader (geometryContext, renderContext);
   },
   getBackShader (geometryContext, renderContext)
   {
      return this .backMaterialNode .getShader (geometryContext, renderContext);
   },
   getRenderModes ()
   {
      return this .renderModeNodes;
   },
   getDepthMode ()
   {
      return this .depthModeNode;
   },
   set_contentScale__ ()
   {
      this .set_pointProperties__ ();
      this .set_applied__ ();
   },
   set_alphaMode__ ()
   {
      this .alphaMode = Shape_AlphaMode .get (this ._alphaMode .getValue ()) ?? Shape_AlphaMode .AUTO;

      this .set_alphaCutoff__ ();
   },
   set_alphaCutoff__ ()
   {
      this .alphaCutoff = this .alphaMode === Shape_AlphaMode .MASK ? this ._alphaCutoff .getValue () : 0;
   },
   set_pointProperties__ ()
   {
      this .stylePropertiesNode [0] = Base_X3DCast (Base_X3DConstants .PointProperties, this ._pointProperties);

      if (this .stylePropertiesNode [0])
         return;

      const browser = this .getBrowser ();

      if (browser .getRenderingProperty ("ContentScale") !== 1)
         this .stylePropertiesNode [0] = browser .getDefaultPointProperties ();
   },
   set_lineProperties__ ()
   {
      this .linePropertiesNode ?._applied .removeInterest ("set_applied__", this);

      this .linePropertiesNode = Base_X3DCast (Base_X3DConstants .LineProperties, this ._lineProperties);

      this .linePropertiesNode ?._applied .addInterest ("set_applied__", this);

      this .set_applied__ ();
   },
   set_applied__ ()
   {
      if (this .linePropertiesNode ?._applied .getValue ())
      {
         this .stylePropertiesNode [1] = this .linePropertiesNode;
      }
      else
      {
         const browser = this .getBrowser ();

         if (browser .getRenderingProperty ("ContentScale") !== 1)
            this .stylePropertiesNode [1] = browser .getDefaultLineProperties ();
         else
            this .stylePropertiesNode [1] = null;
      }
   },
   set_fillProperties__ ()
   {
      this .stylePropertiesNode [2] ?._transparent .removeInterest ("set_transparent__", this);

      this .stylePropertiesNode [2] = Base_X3DCast (Base_X3DConstants .FillProperties, this ._fillProperties);

      this .stylePropertiesNode [2] ?._transparent .addInterest ("set_transparent__", this);

      this .stylePropertiesNode [3] = this .stylePropertiesNode [2];
   },
   set_material__ ()
   {
      if (this .materialNode)
      {
         this .materialNode ._transparent      .removeInterest ("set_transparent__",      this);
         this .materialNode ._transmission     .removeInterest ("set_transmission__",     this);
         this .materialNode ._volumeScatter    .removeInterest ("set_volumeScatter__",    this);
         this .materialNode ._renderedTextures .removeInterest ("set_renderedTextures__", this);
      }

      this .materialNode = Base_X3DCast (Base_X3DConstants .X3DMaterialNode, this ._material)
         ?? this .getBrowser () .getDefaultMaterial ();

      this .materialNode ._transparent      .addInterest ("set_transparent__",      this);
      this .materialNode ._transmission     .addInterest ("set_transmission__",     this);
      this .materialNode ._volumeScatter    .addInterest ("set_volumeScatter__",    this);
      this .materialNode ._renderedTextures .addInterest ("set_renderedTextures__", this);

      this .set_transmission__ ();
      this .set_volumeScatter__ ();

      this .set_transmission__ ();

      // Depreciated TwoSidedMaterial handling.

      if (Base_X3DCast (Base_X3DConstants .TwoSidedMaterial, this .materialNode))
         this .set_backMaterial__ ();
   },
   set_backMaterial__ ()
   {
      if (this .backMaterialNode)
      {
         this .backMaterialNode ._transparent      .removeInterest ("set_transparent__",      this);
         this .backMaterialNode ._transmission     .removeInterest ("set_transmission__",     this);
         this .backMaterialNode ._volumeScatter    .removeInterest ("set_volumeScatter__",    this);
         this .backMaterialNode ._renderedTextures .removeInterest ("set_renderedTextures__", this);
      }

      this .backMaterialNode = Base_X3DCast (Base_X3DConstants .X3DOneSidedMaterialNode, this ._backMaterial);

      if (this .backMaterialNode)
      {
         this .backMaterialNode ._transparent      .addInterest ("set_transparent__",      this);
         this .backMaterialNode ._transmission     .addInterest ("set_transmission__",     this);
         this .backMaterialNode ._volumeScatter    .addInterest ("set_volumeScatter__",    this);
         this .backMaterialNode ._renderedTextures .addInterest ("set_renderedTextures__", this);
      }

      // Depreciated TwoSidedMaterial handling.

      this .backMaterialNode ??= Base_X3DCast (Base_X3DConstants .TwoSidedMaterial, this .materialNode);
   },
   set_texture__ ()
   {
      if (this .textureNode)
      {
         this .textureNode               .removeInterest ("updateTextureBits", this);
         this .textureNode ._transparent .removeInterest ("set_transparent__", this);

         // MultiTexture
         this .textureNode ._renderedTextures ?.removeInterest ("set_renderedTextures__", this);
      }

      this .textureNode = Base_X3DCast (Base_X3DConstants .X3DTextureNode, this ._texture);

      if (this .textureNode)
      {
         this .textureNode               .addInterest ("updateTextureBits", this);
         this .textureNode ._transparent .addInterest ("set_transparent__", this);

         // MultiTexture
         this .textureNode ._renderedTextures ?.addInterest ("set_renderedTextures__", this);

         this .updateTextureBits ();
      }
      else
      {
         this .textureBits .clear ();
      }
   },
   set_textureTransform__ ()
   {
      this .textureTransformNode ?.removeInterest ("updateTextureTransformMapping", this);

      this .textureTransformNode = Base_X3DCast (Base_X3DConstants .X3DTextureTransformNode, this ._textureTransform)
         ?? this .getBrowser () .getDefaultTextureTransform ();

      this .textureTransformNode .addInterest ("updateTextureTransformMapping", this);

      this .updateTextureTransformMapping ();
   },
   updateTextureTransformMapping ()
   {
      this .textureTransformMapping .clear ();

      this .textureTransformNode .getTextureTransformMapping (this .textureTransformMapping);
   },
   set_shaders__ ()
   {
      const shaderNodes = this .shaderNodes;

      for (const shaderNode of shaderNodes)
         shaderNode ._isValid .removeInterest ("set_shader__", this);

      shaderNodes .length = 0;

      for (const node of this ._shaders)
      {
         const shaderNode = Base_X3DCast (Base_X3DConstants .X3DShaderNode, node);

         if (shaderNode)
            shaderNodes .push (shaderNode);
      }

      for (const shaderNode of shaderNodes)
         shaderNode ._isValid .addInterest ("set_shader__", this);

      this .set_shader__ ();
   },
   set_shader__: (() =>
   {
      function getShader ()
      {
         return this .shaderNode;
      }

      return function ()
      {
         const shaderNodes = this .shaderNodes;

         if (this .shaderNode)
         {
            this .shaderNode .deselect ();

            this .shaderNode .removeInterest ("set_renderedTextures__", this);
         }

         this .shaderNode = null;

         for (const shaderNode of shaderNodes)
         {
            if (shaderNode ._isValid .getValue ())
            {
               this .shaderNode = shaderNode;
               break;
            }
         }

         if (this .shaderNode)
         {
            this .shaderNode .select ();

            this .shaderNode .addInterest ("set_renderedTextures__", this);

            this .getShader     = getShader;
            this .getBackShader = getShader;
         }
         else
         {
            delete this .getShader;
            delete this .getBackShader;
         }
      };
   })(),
   set_renderModes__ ()
   {
      this .renderModeNodes .length = 0;

      this .blendModeNode = Base_X3DCast (Base_X3DConstants .BlendMode, this ._blendMode),
      this .depthModeNode = Base_X3DCast (Base_X3DConstants .DepthMode, this ._depthMode);

      if (this .blendModeNode) this .renderModeNodes .push (this .blendModeNode);
      if (this .depthModeNode) this .renderModeNodes .push (this .depthModeNode);
   },
   set_transparent__ ()
   {
      this .setTransparent (this .stylePropertiesNode [3] ?.isTransparent () ||
                            this .materialNode ?.isTransparent () ||
                            this .backMaterialNode ?.isTransparent () ||
                            this .textureNode ?.isTransparent () ||
                            this .blendModeNode);
   },
   set_transmission__ ()
   {
      this .setTransmission (this .materialNode ?.isTransmission () ||
                             this .backMaterialNode ?.isTransmission ());

   },
   set_volumeScatter__ ()
   {
      this .setVolumeScatter (this .materialNode ?.isVolumeScatter () ||
                              this .backMaterialNode ?.isVolumeScatter ());
   },
   set_renderedTextures__ ()
   {
      const { renderedTextures } = this;

      renderedTextures .clear ();

      this .materialNode      .getRenderedTextures (renderedTextures);
      this .backMaterialNode ?.getRenderedTextures (renderedTextures);

      this .textureNode ?.getRenderedTextures (renderedTextures);
      this .shaderNode  ?.getRenderedTextures (renderedTextures);
   },
   traverse (type, renderObject)
   {
      for (const renderedTexture of this .renderedTextures)
         renderedTexture .traverse (type, renderObject);
   },
});

Object .defineProperties (Appearance,
{
   ... Core_X3DNode .getStaticProperties ("Appearance", "Shape", 1, "appearance", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "alphaMode",          new x_ite_Fields .SFString ("AUTO")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "alphaCutoff",        new x_ite_Fields .SFFloat (0.5)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "acousticProperties", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pointProperties",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "lineProperties",     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fillProperties",     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "material",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backMaterial",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "texture",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "textureTransform",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "shaders",            new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "blendMode",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "depthMode",          new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const Appearance_default_ = Appearance;
;

/* harmony default export */ const Shape_Appearance = (Namespace/* default */.A .add ("Appearance", Appearance_default_));
;// ./src/x_ite/Components/Shape/PointProperties.js








function PointProperties (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PointProperties);

   // Private properties

   this .attenuation = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (PointProperties .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   initialize ()
   {
      Shape_X3DAppearanceChildNode .prototype .initialize .call (this);

      const
         browser = this .getBrowser (),
         gl      = browser .getContext ();

      this .pointSizeRange = gl .getParameter (gl .ALIASED_POINT_SIZE_RANGE);

      browser .getRenderingProperties () ._ContentScale .addInterest ("set_contentScale__", this);

      this ._pointSizeScaleFactor .addInterest ("set_pointSizeScaleFactor__", this);
      this ._pointSizeMinValue    .addInterest ("set_pointSizeMinValue__",    this);
      this ._pointSizeMaxValue    .addInterest ("set_pointSizeMaxValue__",    this);
      this ._attenuation          .addInterest ("set_attenuation__",          this);

      this .set_pointSizeScaleFactor__ ();
      this .set_pointSizeMinValue__ ();
      this .set_pointSizeMaxValue__ ();
      this .set_attenuation__ ();
   },
   getStyleKey ()
   {
      return 1;
   },
   set_contentScale__ ()
   {
      this .set_pointSizeScaleFactor__ ();
      this .set_pointSizeMinValue__ ();
      this .set_pointSizeMaxValue__ ();
   },
   set_pointSizeScaleFactor__ ()
   {
      const contentScale = this .getBrowser () .getRenderingProperty ("ContentScale");

      this .pointSizeScaleFactor = Math .max (this ._pointSizeScaleFactor .getValue (), 0) * contentScale;
   },
   set_pointSizeMinValue__ ()
   {
      const contentScale = this .getBrowser () .getRenderingProperty ("ContentScale");

      this .pointSizeMinValue = Math_Algorithm .clamp (this ._pointSizeMinValue .getValue (), 0, this .pointSizeRange [1]) * contentScale;
   },
   set_pointSizeMaxValue__ ()
   {
      const contentScale = this .getBrowser () .getRenderingProperty ("ContentScale");

      this .pointSizeMaxValue = Math_Algorithm .clamp (this ._pointSizeMaxValue .getValue (), 0, this .pointSizeRange [1]) * contentScale;
   },
   set_attenuation__ ()
   {
      this .attenuation [0] = Math .max (0, this ._attenuation [0]);
      this .attenuation [1] = Math .max (0, this ._attenuation [1]);
      this .attenuation [2] = Math .max (0, this ._attenuation [2]);
   },
   setShaderUniforms (gl, shaderObject)
   {
      gl .uniform1f  (shaderObject .x3d_PointPropertiesPointSizeScaleFactor, this .pointSizeScaleFactor);
      gl .uniform1f  (shaderObject .x3d_PointPropertiesPointSizeMinValue,    this .pointSizeMinValue);
      gl .uniform1f  (shaderObject .x3d_PointPropertiesPointSizeMaxValue,    this .pointSizeMaxValue);
      gl .uniform3fv (shaderObject .x3d_PointPropertiesAttenuation,          this .attenuation);
   },
});

Object .defineProperties (PointProperties,
{
   ... Core_X3DNode .getStaticProperties ("PointProperties", "Shape", 5, "pointProperties", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pointSizeScaleFactor", new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pointSizeMinValue",    new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pointSizeMaxValue",    new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "attenuation",          new x_ite_Fields .SFVec3f (1, 0, 0)),
      ]),
      enumerable: true,
   },
});

const PointProperties_default_ = PointProperties;
;

/* harmony default export */ const Shape_PointProperties = (Namespace/* default */.A .add ("PointProperties", PointProperties_default_));
;// ./src/x_ite/Components/Shape/LineProperties.js







function LineProperties (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .LineProperties);
}

Object .assign (Object .setPrototypeOf (LineProperties .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   initialize ()
   {
      Shape_X3DAppearanceChildNode .prototype .initialize .call (this);

      const browser = this .getBrowser ();

      browser .getRenderingProperties () ._ContentScale .addInterest ("set_linewidthScaleFactor__", this);

      this ._applied              .addInterest ("set_applied__",              this);
      this ._linetype             .addInterest ("set_linetype__",             this);
      this ._linewidthScaleFactor .addInterest ("set_linewidthScaleFactor__", this);

      this .set_applied__ ();
      this .set_linetype__ ();
      this .set_linewidthScaleFactor__ ();

      // Preload texture.
      browser .getLinetypeTexture ();
   },
   getStyleKey ()
   {
      return this .applied ? 2 : 1;
   },
   getApplied ()
   {
      return this .applied;
   },
   getLinetype ()
   {
      return this .linetype;
   },
   getLinewidthScaleFactor ()
   {
      return this .linewidthScaleFactor;
   },
   getTransformLines ()
   {
      return this .transformLines;
   },
   set_applied__ ()
   {
      this .applied = this ._applied .getValue ();
   },
   set_linetype__ ()
   {
      let linetype = this ._linetype .getValue ();

      if (linetype < 1 || linetype > 16)
         linetype = 1;

      this .linetype = linetype;
   },
   set_linewidthScaleFactor__ ()
   {
      const
         browser               = this .getBrowser (),
         gl                    = browser .getContext (),
         contentScale          = browser .getRenderingProperty ("ContentScale"),
         aliasedLineWidthRange = gl .getParameter (gl .ALIASED_LINE_WIDTH_RANGE);

      this .linewidthScaleFactor = Math .max (1, this ._linewidthScaleFactor .getValue ()) * contentScale;
      this .transformLines       = this .linewidthScaleFactor > 1 && this .linewidthScaleFactor > aliasedLineWidthRange [1];
   },
   setShaderUniforms (gl, shaderObject)
   {
      const browser = this .getBrowser ();

      if (this .applied)
      {
         const lineStippleScale = 1 / (browser .getRenderingProperty ("PixelsPerPoint") * browser .getRenderingProperty ("ContentScale") * 32); // 32px

         const textureUnit = browser .popTextureUnit ();

         if (!this .transformLines)
            gl .lineWidth (this .linewidthScaleFactor);

         gl .uniform1i (shaderObject .x3d_LinePropertiesLinetype, this .linetype);
         gl .uniform1f (shaderObject .x3d_LineStippleScale,       lineStippleScale);

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (gl .TEXTURE_2D, browser .getLinetypeTexture () .getTexture ());
         gl .uniform1i (shaderObject .x3d_LinePropertiesTexture, textureUnit);
      }
      else
      {
         if (!this .transformLines)
            gl .lineWidth (browser .getRenderingProperty ("ContentScale"));

         gl .uniform1i (shaderObject .x3d_LinePropertiesLinetype, 16);
         gl .uniform1f (shaderObject .x3d_LineStippleScale,       1);
      }
   },
});

Object .defineProperties (LineProperties,
{
   ... Core_X3DNode .getStaticProperties ("LineProperties", "Shape", 2, "lineProperties", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "applied",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "linetype",             new x_ite_Fields .SFInt32 (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "linewidthScaleFactor", new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const LineProperties_default_ = LineProperties;
;

/* harmony default export */ const Shape_LineProperties = (Namespace/* default */.A .add ("LineProperties", LineProperties_default_));
;// ./src/x_ite/Components/Shape/X3DMaterialNode.js








function X3DMaterialNode (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DMaterialNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent",      new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "transmission",     new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "volumeScatter",    new x_ite_Fields .SFBool (),
                          Base_X3DConstants .outputOnly, "renderedTextures", new x_ite_Fields .SFTime ());

   // Private properties

   this .textureBits  = new Utility_BitSet ();
   this .textureNodes = [ ];
   this .shaderNodes  = this .getBrowser () .getShaders ();
}

Object .assign (Object .setPrototypeOf (X3DMaterialNode .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   isPhysical ()
   {
      return false;
   },
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
   setTransmission (value)
   {
      if (!!value !== this ._transmission .getValue ())
         this ._transmission = value;
   },
   isTransmission ()
   {
      return this ._transmission .getValue ();
   },
   setVolumeScatter (value)
   {
      if (!!value !== this ._volumeScatter .getValue ())
         this ._volumeScatter = value;
   },
   isVolumeScatter ()
   {
      return this ._volumeScatter .getValue ();
   },
   addTexture (index, textureNode)
   {
      // Collect textures.

      this .textureNodes [index] = textureNode;

      this ._renderedTextures = this .getBrowser () .getCurrentTime ();

      // Set texture bits.

      index *= 4;

      this .textureBits .remove (index, 0xf);
      this .textureBits .add (index, textureNode ?.getTextureBits () ?? 0);
   },
   getTextureBits ()
   {
      return this .textureBits;
   },
   getRenderedTextures (renderedTextures)
   {
      for (const textureNode of this .textureNodes)
         textureNode ?.getRenderedTextures (renderedTextures);
   },
   getShader (geometryContext, renderContext)
   {
      let key = "";

      key += this .textureBits .toString (16);
      key += ".";
      key += this .getMaterialKey ();
      key += geometryContext .geometryKey;

      if (renderContext)
      {
         const { renderObject, shadows, fogNode, shapeNode, appearanceNode, textureNode, hAnimNode, localObjectsKeys } = renderContext;

         key += shapeNode .getAlphaMode ();
         key += renderObject .getRenderKey ();
         key += shadows || renderObject .getGlobalShadow () ? 1 : 0;
         key += fogNode ?.getFogType () ?? 0;
         key += shapeNode .getShapeKey ();
         key += appearanceNode .getStyleProperties (geometryContext .geometryType) ?.getStyleKey () ?? 0;
         key += appearanceNode .getTextureTransformMapping () .size || 1;
         key += geometryContext .textureCoordinateMapping .size || 1;
         key += hAnimNode ?.getHAnimKey () ?? "[]";
         key += localObjectsKeys .join (""); // ClipPlane, X3DLightNode
         key += ".";
         key += textureNode ? 2 : appearanceNode .getTextureBits () .toString (16);
      }
      else
      {
         // Rubberband, X3DBackgroundNode

         const { renderObject, alphaMode, textureNode, localObjectsKeys } = geometryContext;

         key += alphaMode;
         key += renderObject .getRenderKey ();
         key += "000011[]";
         key += localObjectsKeys .join (""); // ClipPlane
         key += ".";
         key += textureNode ?.getTextureBits () .toString (16) ?? 0;
      }

      return this .shaderNodes .get (key)
         ?? this .createShader (key, geometryContext, renderContext);
   },
   getShaderOptions (geometryContext, renderContext)
   {
      const
         browser = this .getBrowser (),
         options = [ ];

      if (browser .getRenderingProperty ("XRSession"))
         options .push ("X3D_XR_SESSION");

      switch (browser .getBrowserOption ("ColorSpace") .toUpperCase ())
      {
         case "SRGB":
            options .push ("X3D_COLORSPACE_SRGB");
            break;
         case "LINEAR":
            options .push ("X3D_COLORSPACE_LINEAR");
            break;
         default: // LINEAR_WHEN_PHYSICAL_MATERIAL
            options .push (this .isPhysical () ? "X3D_COLORSPACE_LINEAR" : "X3D_COLORSPACE_SRGB");
            break;
      }

      switch (browser .getBrowserOption ("ToneMapping") .toUpperCase ())
      {
         default: // NONE
            break;
         case "ACES_NARKOWICZ":
         case "ACES_HILL":
         case "ACES_HILL_EXPOSURE_BOOST":
         case "KHR_PBR_NEUTRAL":
            options .push (`X3D_TONEMAP_${browser .getBrowserOption ("ToneMapping") .toUpperCase ()}`);
            break;
      }

      options .push (`X3D_GEOMETRY_${geometryContext .geometryType}D`);

      if (geometryContext .hasFogCoords)
         options .push ("X3D_FOG_COORDS");

      if (geometryContext .colorMaterial)
         options .push ("X3D_COLOR_MATERIAL");

      const flat = browser .getRenderingProperty ("Shading") === "FLAT";

      if (geometryContext .hasNormals && !flat)
         options .push ("X3D_NORMALS");

      if (geometryContext .hasTangents)
         options .push ("X3D_TANGENTS");

      if (renderContext)
      {
         const { renderObject, fogNode, shapeNode, appearanceNode, hAnimNode, localObjectsKeys, textureNode } = renderContext;

         this .addRenderOptions (options, renderObject, shapeNode .getAlphaMode ());

         switch (fogNode ?.getFogType ())
         {
            case 1:
               options .push ("X3D_FOG", "X3D_FOG_LINEAR");
               break;
            case 2:
               options .push ("X3D_FOG", "X3D_FOG_EXPONENTIAL");
               break;
         }

         hAnimNode ?.getShaderOptions (options);

         const
            localLights          = renderContext .localObjects .filter (o => o .lightNode),
            lights               = renderObject .getGlobalLights () .concat (localLights),
            objectsKeys          = renderObject .getGlobalLightsKeys () .concat (localObjectsKeys),
            numClipPlanes        = objectsKeys .reduce ((a, k) => a + (k === 0), 0),
            numLights            = objectsKeys .reduce ((a, k) => a + (k === 1), 0),
            numEnvironmentLights = objectsKeys .reduce ((a, k) => a + k .toString () .startsWith ("[2."), 0),
            numTextureProjectors = objectsKeys .reduce ((a, k) => a + k .toString () .startsWith ("[3."), 0);

         if (numClipPlanes)
         {
            options .push ("X3D_CLIP_PLANES")
            options .push (`X3D_NUM_CLIP_PLANES ${Math .min (numClipPlanes, browser .getMaxClipPlanes ())}`);
         }

         if (numLights && geometryContext .hasNormals)
         {
            options .push ("X3D_LIGHTING")
            options .push (`X3D_NUM_LIGHTS ${Math .min (numLights, browser .getMaxLights ())}`);

            if (renderContext .shadows || renderObject .getGlobalShadows () .at (-1))
               options .push ("X3D_SHADOWS", "X3D_PCF_FILTERING");
         }

         if (numEnvironmentLights && geometryContext .hasNormals)
         {
            const
               container = lights .find (c => c .lightNode .getLightKey () .toString () .startsWith ("[2.")),
               lightNode = container .lightNode;

            // Although we count this kind of light here, only one is supported.
            options .push ("X3D_USE_IBL")
            options .push (`X3D_NUM_ENVIRONMENT_LIGHTS ${Math .min (numEnvironmentLights, browser .getMaxEnvironmentLights ())}`);

            if (lightNode .getDiffuseTexture () ?.isLinear ())
               options .push ("X3D_ENVIRONMENT_DIFFUSE_TEXTURE_LINEAR");

            if (lightNode .getSpecularTexture () ?.isLinear ())
               options .push ("X3D_ENVIRONMENT_SPECULAR_TEXTURE_LINEAR");

            if (lightNode .getSheenTexture () ?.isLinear ())
               options .push ("X3D_ENVIRONMENT_SHEEN_TEXTURE_LINEAR");
         }

         if (numTextureProjectors)
         {
            const textureProjectors = lights .filter (c => c .lightNode .getLightKey () .toString () .startsWith ("[3."));

            options .push ("X3D_TEXTURE_PROJECTION")
            options .push (`X3D_NUM_TEXTURE_PROJECTORS ${Math .min (numTextureProjectors, browser .getMaxTextures ())}`);

            for (const [i, textureProjector] of textureProjectors .entries ())
            {
               if (textureProjector .lightNode .getTexture () .isLinear ())
                  options .push (`X3D_TEXTURE_PROJECTOR_${i}_LINEAR`);
            }
         }

         if (appearanceNode .getStyleProperties (geometryContext .geometryType))
         {
            options .push ("X3D_STYLE_PROPERTIES");

            if (appearanceNode .getStyleProperties (geometryContext .geometryType) .getStyleKey () > 1)
               options .push ("X3D_STYLE_PROPERTIES_TEXTURE");
         }

         if (+this .textureBits)
            options .push ("X3D_MATERIAL_TEXTURES");

         if (textureNode)
         {
            // ScreenText PixelTexture.

            options .push ("X3D_TEXTURE",
                           "X3D_NUM_TEXTURES 1",
                           "X3D_NUM_TEXTURE_TRANSFORMS 1",
                           "X3D_NUM_TEXTURE_COORDINATES 1");

            textureNode .getShaderOptions (options, 0);
         }
         else
         {
            if (+appearanceNode .getTextureBits () && !this .getBaseTexture ())
            {
               const
                  textureNode  = appearanceNode .getTexture (),
                  textureCount = textureNode .getCount ();

               if (textureCount)
               {
                  options .push ("X3D_TEXTURE");
                  options .push (`X3D_NUM_TEXTURES ${textureCount}`);

                  if (textureNode .getType () .includes (Base_X3DConstants .MultiTexture))
                     options .push ("X3D_MULTI_TEXTURING");

                  textureNode .getShaderOptions (options);
               }
            }

            options .push (`X3D_NUM_TEXTURE_TRANSFORMS ${appearanceNode .getTextureTransformMapping () .size || 1}`);
            options .push (`X3D_NUM_TEXTURE_COORDINATES ${geometryContext .textureCoordinateMapping .size || 1}`);
         }

         switch (shapeNode .getShapeKey ())
         {
            case 1:
               options .push ("X3D_INSTANCING");
               break;
            case 2:
               options .push ("X3D_INSTANCING", "X3D_TEX_COORD_RAMP");
               break;
            case 3:
               options .push ("X3D_INSTANCING", "X3D_INSTANCE_NORMAL");
               break;
         }
      }
      else
      {
         const { renderObject, alphaMode, localObjectsKeys, textureNode } = geometryContext;

         this .addRenderOptions (options, renderObject, alphaMode);

         const numClipPlanes = localObjectsKeys .reduce ((a, k) => a + (k === 0), 0);

         if (numClipPlanes)
         {
            options .push ("X3D_CLIP_PLANES")
            options .push (`X3D_NUM_CLIP_PLANES ${Math .min (numClipPlanes, browser .getMaxClipPlanes ())}`);
         }

         if (textureNode)
         {
            // X3DBackgroundNode textures

            options .push ("X3D_TEXTURE",
                           "X3D_NUM_TEXTURES 1",
                           "X3D_NUM_TEXTURE_TRANSFORMS 1",
                           "X3D_NUM_TEXTURE_COORDINATES 1");

            textureNode .getShaderOptions (options, 0);
         }
      }

      return options;
   },
   addRenderOptions (options, renderObject, alphaMode)
   {
      switch (renderObject .getRenderPass ())
      {
         case Rendering_RenderPass .VOLUME_SCATTER_KEY:
         {
            options .push ("X3D_VOLUME_SCATTER_PASS", "X3D_LINEAR_OUTPUT");
            break;
         }
         case Rendering_RenderPass .TRANSMISSION_KEY:
         {
            options .push ("X3D_TRANSMISSION_PASS", "X3D_LINEAR_OUTPUT");
            break;
         }
      }

      if (renderObject .getLogarithmicDepthBuffer ())
         options .push ("X3D_LOGARITHMIC_DEPTH_BUFFER");

      switch (alphaMode)
      {
         case Shape_AlphaMode .OPAQUE:
         {
            options .push ("X3D_ALPHA_MODE_OPAQUE");
            break;
         }
         case Shape_AlphaMode .MASK:
         {
            options .push ("X3D_ALPHA_MODE_MASK");
            break;
         }
         case Shape_AlphaMode .BLEND:
         {
            options .push ("X3D_ALPHA_MODE_BLEND");

            if (renderObject .getRenderPass () === Rendering_RenderPass .RENDER_KEY)
            {
               if (renderObject .getOrderIndependentTransparency ())
                  options .push ("X3D_ORDER_INDEPENDENT_TRANSPARENCY");
            }

            break;
         }
      }
   },
});

Object .defineProperties (X3DMaterialNode, Core_X3DNode .getStaticProperties ("X3DMaterialNode", "Shape", 1));

const X3DMaterialNode_default_ = X3DMaterialNode;
;

/* harmony default export */ const Shape_X3DMaterialNode = (Namespace/* default */.A .add ("X3DMaterialNode", X3DMaterialNode_default_));
;// ./src/x_ite/Components/Shape/X3DOneSidedMaterialNode.js






function X3DOneSidedMaterialNode (executionContext)
{
   Shape_X3DMaterialNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DOneSidedMaterialNode);

   // Private properties

   this .emissiveColorArray = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (X3DOneSidedMaterialNode .prototype, Shape_X3DMaterialNode .prototype),
{
   initialize ()
   {
      Shape_X3DMaterialNode .prototype .initialize .call (this);

      this ._emissiveColor   .addInterest ("set_emissiveColor__",   this);
      this ._emissiveTexture .addInterest ("set_emissiveTexture__", this);
      this ._normalScale     .addInterest ("set_normalScale__",     this);
      this ._normalTexture   .addInterest ("set_normalTexture__",   this);
      this ._transparency    .addInterest ("set_transparency__",    this);
      this ._transparency    .addInterest ("set_transparent__",     this);

      this .set_emissiveColor__ ();
      this .set_emissiveTexture__ ();
      this .set_normalScale__ ();
      this .set_normalTexture__ ();
      this .set_transparency__ ();
   },
   set_emissiveColor__ ()
   {
      this .emissiveColorArray .set (this ._emissiveColor .getValue ());
   },
   set_emissiveTexture__ ()
   {
      const index = this .getTextureIndices () .EMISSIVE_TEXTURE;

      this .emissiveTextureNode ?._linear .removeInterest ("addTexture", this);

      this .emissiveTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._emissiveTexture);

      this .emissiveTextureNode ?._linear .addInterest ("addTexture", this, index, this .emissiveTextureNode);

      this .addTexture (index, this .emissiveTextureNode);
   },
   set_normalScale__ ()
   {
      this .normalScale = Math .max (this ._normalScale .getValue (), 0);
   },
   set_normalTexture__ ()
   {
      this .normalTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._normalTexture);

      this .addTexture (this .getTextureIndices () .NORMAL_TEXTURE, this .normalTextureNode);
   },
   set_transparency__ ()
   {
      this .transparency = Math_Algorithm .clamp (this ._transparency .getValue (), 0, 1);
   },
   set_transparent__ ()
   {
      this .setTransparent (this .transparency || this .getBaseTexture () ?.isTransparent ());
   },
   getBaseTexture ()
   {
      return this .emissiveTextureNode;
   },
   getShaderOptions (geometryContext, renderContext)
   {
      const options = Shape_X3DMaterialNode .prototype .getShaderOptions .call (this, geometryContext, renderContext);

      if (+this .getTextureBits ())
      {
         this .emissiveTextureNode ?.getShaderOptions (options, "EMISSIVE");
         this .normalTextureNode   ?.getShaderOptions (options, "NORMAL");
      }

      return options;
   },
   setShaderUniforms (gl, shaderObject, textureTransformMapping, textureCoordinateMapping)
   {
      gl .uniform3fv (shaderObject .x3d_EmissiveColor, this .emissiveColorArray);
      gl .uniform1f  (shaderObject .x3d_Transparency,  this .transparency);

      if (!+this .getTextureBits ())
         return;

      this .emissiveTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_EmissiveTexture,
         this ._emissiveTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      if (this .normalTextureNode)
         gl .uniform1f (shaderObject .x3d_NormalScale, this .normalScale);

      this .normalTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_NormalTexture,
         this ._normalTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);
   },
});

Object .defineProperties (X3DOneSidedMaterialNode, Core_X3DNode .getStaticProperties ("X3DOneSidedMaterialNode", "Shape", 4));

const X3DOneSidedMaterialNode_default_ = X3DOneSidedMaterialNode;
;

/* harmony default export */ const Shape_X3DOneSidedMaterialNode = (Namespace/* default */.A .add ("X3DOneSidedMaterialNode", X3DOneSidedMaterialNode_default_));
;// ./src/x_ite/Components/Shape/UnlitMaterial.js







function UnlitMaterial (executionContext)
{
   Shape_X3DOneSidedMaterialNode .call (this, executionContext);

   this .addType (Base_X3DConstants .UnlitMaterial);
}

Object .assign (Object .setPrototypeOf (UnlitMaterial .prototype, Shape_X3DOneSidedMaterialNode .prototype),
{
   initialize ()
   {
      Shape_X3DOneSidedMaterialNode .prototype .initialize .call (this);

      this ._emissiveTexture .addInterest ("set_transparent__", this);

      this .set_transparent__ ();
   },
   getMaterialKey ()
   {
      return 0;
   },
   getTextureIndices: (() =>
   {
      let i = 0;

      const textureIndices = {
         EMISSIVE_TEXTURE: i ++,
         NORMAL_TEXTURE: i ++,
      };

      return function ()
      {
         return textureIndices;
      };
   })(),
   set_emissiveTexture__ ()
   {
      this .getBaseTexture () ?._transparent .removeInterest ("set_transparent__", this);

      Shape_X3DOneSidedMaterialNode .prototype .set_emissiveTexture__ .call (this);

      this .getBaseTexture () ?._transparent .addInterest ("set_transparent__", this);
   },
   createShader (key, geometryContext, renderContext)
   {
      const
         browser = this .getBrowser (),
         options = this .getShaderOptions (geometryContext, renderContext);

      options .push ("X3D_UNLIT_MATERIAL");

      const shaderNode = browser .createShader ({
         name: "Unlit",
         vertexShader: "Default",
         fragmentShader: "Unlit",
         options,
      });

      browser .getShaders () .set (key, shaderNode);

      return shaderNode;
   },
});

Object .defineProperties (UnlitMaterial,
{
   ... Core_X3DNode .getStaticProperties ("UnlitMaterial", "Shape", 1, "material", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",               new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveColor",          new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTextureMapping", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTexture",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalScale",            new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTextureMapping",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTexture",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency",           new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const UnlitMaterial_default_ = UnlitMaterial;
;

/* harmony default export */ const Shape_UnlitMaterial = (Namespace/* default */.A .add ("UnlitMaterial", UnlitMaterial_default_));
;// ./src/x_ite/Browser/Shape/X3DShapeContext.js








const
   _linetypeTextures                 = Symbol (),
   _hatchStyleTextures               = Symbol (),
   _defaultAppearance                = Symbol (),
   _defaultPointProperties           = Symbol (),
   _defaultLineProperties            = Symbol (),
   _defaultMaterial                  = Symbol (),
   _lineFillTextureProperties        = Symbol (),
   _lineTransformShaderNode          = Symbol (),
   _lineTransformInstancedShaderNode = Symbol (),
   _lineTransformFeedback            = Symbol ();

function X3DShapeContext ()
{
   this [_lineTransformInstancedShaderNode] = [ ];
   this [_hatchStyleTextures]               = [ ];
}

Object .assign (X3DShapeContext .prototype,
{
   getDefaultAppearance ()
   {
      return this [_defaultAppearance] ??= (() =>
      {
         const defaultAppearance = new Shape_Appearance (this .getPrivateScene ());

         defaultAppearance .setup ();

         return defaultAppearance;
      })();
   },
   getDefaultPointProperties ()
   {
      return this [_defaultPointProperties] ??= (() =>
      {
         const defaultPointProperties = new Shape_PointProperties (this .getPrivateScene ());

         defaultPointProperties .setup ();

         return defaultPointProperties;
      })();
   },
   getDefaultLineProperties ()
   {
      return this [_defaultLineProperties] ??= (() =>
      {
         const defaultLineProperties = new Shape_LineProperties (this .getPrivateScene ());

         defaultLineProperties ._applied = false;

         defaultLineProperties .setup ();

         return defaultLineProperties;
      })();
   },
   getDefaultMaterial ()
   {
      return this [_defaultMaterial] ??= (() =>
      {
         const defaultMaterial = new Shape_UnlitMaterial (this .getPrivateScene ());

         defaultMaterial .setup ();

         return defaultMaterial;
      })();
   },
   getLinetypeTexture ()
   {
      return this [_linetypeTextures] ??= (() =>
      {
         const linetypeTextures = new Texturing_ImageTexture (this .getPrivateScene ());

         linetypeTextures ._url [0]           = Networking_URLs .getLinetypeURL ();
         linetypeTextures ._textureProperties = this .getLineFillTextureProperties ();

         linetypeTextures .setup ();

         return linetypeTextures;
      })();
   },
   getHatchStyleTexture (index)
   {
      return this [_hatchStyleTextures] [index] ??= (() =>
      {
         const hatchStyleTexture = new Texturing_ImageTexture (this .getPrivateScene ());

         hatchStyleTexture ._url [0]           = Networking_URLs .getHatchingURL (index);
         hatchStyleTexture ._textureProperties = this .getLineFillTextureProperties ();

         hatchStyleTexture .setup ();

         return hatchStyleTexture;
      })();
   },
   getLineFillTextureProperties ()
   {
      return this [_lineFillTextureProperties] ??= (() =>
      {
         const lineFillTextureProperties = new Texturing_TextureProperties (this .getPrivateScene ());

         lineFillTextureProperties ._minificationFilter  = "NEAREST_PIXEL";
         lineFillTextureProperties ._magnificationFilter = "NEAREST_PIXEL";
         lineFillTextureProperties ._textureCompression  = "DEFAULT";

         lineFillTextureProperties .setup ();

         return lineFillTextureProperties;
      })();
   },
   getLineTransformShader ()
   {
      return this [_lineTransformShaderNode] ??= this .createLineTransformShader (0, false);
   },
   getLineTransformInstancedShader (pass)
   {
      return this [_lineTransformInstancedShaderNode] [pass] ??= this .createLineTransformShader (pass, true);
   },
   createLineTransformShader (pass, instanced)
   {
      const options = [`X3D_PASS_${pass}`];

      if (instanced)
         options .push ("X3D_INSTANCING");

      const uniforms = [
         [
            "viewport",
            "modelViewProjectionMatrix",
            "invModelViewProjectionMatrix",
            "linewidthScaleFactor1_2",
         ],
         [ ],
         [ ],
      ]
      [pass];

      const transformFeedbackVaryings = [
         [
            "coordIndex0", "lineStipple0", "fogDepth0", "color0", "vertex0",
            "coordIndex1", "lineStipple1", "fogDepth1", "color1", "vertex1",
            "coordIndex2", "lineStipple2", "fogDepth2", "color2", "vertex2",
         ],
         [
            "instanceMatrix0",
            "instanceMatrix1",
            "instanceMatrix2",
         ],
         [
            "instanceNormalMatrix0", "tangent0", "normal0",
            "instanceNormalMatrix1", "tangent1", "normal1",
            "instanceNormalMatrix2", "tangent2", "normal2",
         ],
      ]
      [pass];

      return this .createShader ({
         name: `LineTransform${instanced ? "Instanced" : ""}`,
         vertexShader: "LineTransform",
         fragmentShader: "LineTransform",
         options,
         uniforms,
         transformFeedbackVaryings,
      });
   },
   getLineTransformFeedback ()
   {
      return this [_lineTransformFeedback] ??= (() =>
      {
         const gl = this .getContext ();

         const lineTransformFeedback = gl .createTransformFeedback ();

         return lineTransformFeedback;
      })();
   },
});

const X3DShapeContext_default_ = X3DShapeContext;
;

/* harmony default export */ const Shape_X3DShapeContext = (Namespace/* default */.A .add ("X3DShapeContext", X3DShapeContext_default_));
;// ./src/x_ite/Components/Sound/X3DSoundNode.js




function X3DSoundNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSoundNode);
}

Object .setPrototypeOf (X3DSoundNode .prototype, Core_X3DChildNode .prototype);

Object .defineProperties (X3DSoundNode, Core_X3DNode .getStaticProperties ("X3DSoundNode", "Sound", 1));

const X3DSoundNode_default_ = X3DSoundNode;
;

/* harmony default export */ const Sound_X3DSoundNode = (Namespace/* default */.A .add ("X3DSoundNode", X3DSoundNode_default_));
;// ./src/x_ite/Components/Sound/PeriodicWave.js







function PeriodicWave (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PeriodicWave);

   this .real = new Float32Array (2);
   this .imag = new Float32Array (2);
}

Object .assign (Object .setPrototypeOf (PeriodicWave .prototype, Sound_X3DSoundNode .prototype),
{
   createPeriodicWave ()
   {
      const
         audioContext = this .getBrowser () .getAudioContext (),
         optionsReal  = this ._optionsReal .shrinkToFit (),
         optionsImag  = this ._optionsImag .shrinkToFit (),
         length       = Math .max (optionsReal .length, optionsImag .length, 2);

      if (this .real .length !== length)
      {
         this .real = new Float32Array (length);
         this .imag = new Float32Array (length);
      }

      this .real .set (optionsReal);
      this .imag .set (optionsImag);

      return audioContext .createPeriodicWave (this .real, this .imag);
   },
});

Object .defineProperties (PeriodicWave,
{
   ... Core_X3DNode .getStaticProperties ("PeriodicWave", "Sound", 2, "periodicWave", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "type",          new x_ite_Fields .SFString ("SQUARE")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "optionsReal",   new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "optionsImag",   new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const PeriodicWave_default_ = PeriodicWave;
;

/* harmony default export */ const Sound_PeriodicWave = (Namespace/* default */.A .add ("PeriodicWave", PeriodicWave_default_));
;// ./src/x_ite/Browser/Sound/X3DSoundContext.js
/* provided dependency */ var X3DSoundContext_$ = __webpack_require__(44).A;



const
   _audioElements       = Symbol (),
   _audioContext        = Symbol (),
   _defaultPeriodicWave = Symbol (),
   _noSoundButton       = Symbol (),
   _noSoundButtonId     = Symbol ();

function X3DSoundContext ()
{
   this [_audioElements] = new Map ();
}

Object .assign (X3DSoundContext .prototype,
{
   initialize ()
   {
      const id = `X3DSoundContext-${this .getId ()}`;

      const events = [
         "blur",
         "click",
         "contextmenu",
         "dblclick",
         "focus",
         "keydown",
         "keyup",
         "mousedown",
         "mouseup",
         "mousewheel",
         "pointerup",
         "touchend",
         "touchmove",
         "touchstart",
      ]
      .map (event => `${event}.${id}`);

      this .getCanvas () .on (events .join (" "), () => this .startAudioElements ());
   },
   getAudioContext ()
   {
      return this [_audioContext] ??= (() =>
      {
         const audioContext = new AudioContext ();

         this .startAudioElement (audioContext, "resume");

         return audioContext;
      })();
   },
   getDefaultPeriodicWave ()
   {
      return this [_defaultPeriodicWave] ??= (() =>
      {
         const defaultPeriodicWave = new Sound_PeriodicWave (this .getPrivateScene ());

         defaultPeriodicWave .setup ();

         return defaultPeriodicWave;
      })();
   },
   startAudioElement (audioElement, functionName = "play", sound = true)
   {
      if (!audioElement)
         return;

      this [_audioElements] .set (audioElement, functionName);

      this .startAudioElements ();
   },
   startAudioElements ()
   {
      for (const [audioElement, functionName] of this [_audioElements])
      {
         audioElement [functionName] ()
            .then (() => this [_audioElements] .delete (audioElement))
            .catch (Function .prototype)
            .finally (() => this .toggleNoSoundButton ());
      }
   },
   stopAudioElement (audioElement, functionName = "pause")
   {
      if (!audioElement)
         return;

      this [_audioElements] .delete (audioElement);

      audioElement [functionName] ();

      this .toggleNoSoundButton ();
   },
   toggleNoSoundButton ()
   {
      clearTimeout (this [_noSoundButtonId]);

      this [_noSoundButtonId] = setTimeout (async () =>
      {
         this [_noSoundButton] ??= X3DSoundContext_$("<div></div>")
            .attr ("part", "no-sound-button")
            .attr ("title", gettext("Activate sound."))
            .addClass (["x_ite-private-no-sound-button", "x_ite-private-button"])
            .on ("mouseup touchend", () => this .startAudioElements ())
            .appendTo (this .getSurface () .find (".x_ite-private-buttons"));

         const
            count = !!this [_audioElements] .size,
            fade  = count ? "x_ite-private-fade-in-300" : "x_ite-private-fade-out-300";

         if (count)
            this [_noSoundButton] .show ();

         this [_noSoundButton] .addClass (fade);

         await X3DSoundContext_$.sleep (400);

         this [_noSoundButton] .removeClass (fade);

         if (count !== !!this [_audioElements] .size)
            return;

         if (!count)
            this [_noSoundButton] .hide ();
      },
      200);
   },
});

const X3DSoundContext_default_ = X3DSoundContext;
;

/* harmony default export */ const Sound_X3DSoundContext = (Namespace/* default */.A .add ("X3DSoundContext", X3DSoundContext_default_));
;// ./src/x_ite/Components/Texturing/X3DTextureTransformNode.js




function X3DTextureTransformNode (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTextureTransformNode);
}

Object .setPrototypeOf (X3DTextureTransformNode .prototype, Shape_X3DAppearanceChildNode .prototype);

Object .defineProperties (X3DTextureTransformNode, Core_X3DNode .getStaticProperties ("X3DTextureTransformNode", "Texturing", 1));

const X3DTextureTransformNode_default_ = X3DTextureTransformNode;
;

/* harmony default export */ const Texturing_X3DTextureTransformNode = (Namespace/* default */.A .add ("X3DTextureTransformNode", X3DTextureTransformNode_default_));
;// ./src/x_ite/Components/Texturing/X3DSingleTextureTransformNode.js





function X3DSingleTextureTransformNode (executionContext)
{
   Texturing_X3DTextureTransformNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSingleTextureTransformNode);

   // Private properties

   this .matrixArray = new Float32Array (Numbers_Matrix4 .IDENTITY);
}

Object .assign (Object .setPrototypeOf (X3DSingleTextureTransformNode .prototype, Texturing_X3DTextureTransformNode .prototype),
{
   getCount ()
   {
      return 1;
   },
   getMatrix ()
   {
      return this .matrixArray;
   },
   setMatrix (value)
   {
      this .matrixArray .set (value);
   },
   getTextureTransformMapping (textureTransformMapping, channel = 0)
   {
      textureTransformMapping .set (this ._mapping .getValue () || channel, channel);
   },
   setShaderUniforms (gl, shaderObject, channel = 0)
   {
      gl .uniformMatrix4fv (shaderObject .x3d_TextureMatrix [channel], false, this .matrixArray);
   },
   transformPoint (texCoord)
   {
      return Numbers_Matrix4 .prototype .multVecMatrix .call (this .matrixArray, texCoord);
   },
});

Object .defineProperties (X3DSingleTextureTransformNode, Core_X3DNode .getStaticProperties ("X3DSingleTextureTransformNode", "Texturing", 1));

const X3DSingleTextureTransformNode_default_ = X3DSingleTextureTransformNode;
;

/* harmony default export */ const Texturing_X3DSingleTextureTransformNode = (Namespace/* default */.A .add ("X3DSingleTextureTransformNode", X3DSingleTextureTransformNode_default_));
;// ./src/x_ite/Components/Texturing/TextureTransform.js










function TextureTransform (executionContext)
{
   Texturing_X3DSingleTextureTransformNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TextureTransform);

   // Units

   this ._rotation .setUnit ("angle");

   // Private properties

   this .matrix = new Numbers_Matrix4 ();
}

Object .assign (Object .setPrototypeOf (TextureTransform .prototype, Texturing_X3DSingleTextureTransformNode .prototype),
{
   initialize ()
   {
      Texturing_X3DSingleTextureTransformNode .prototype .initialize .call (this);

      this .addInterest ("eventsProcessed", this);

      this .eventsProcessed ();
   },
   eventsProcessed: (() =>
   {
      const
         vector  = new Numbers_Vector2 (),
         matrix3 = new Numbers_Matrix3 ();

      return function ()
      {
         const
            translation = this ._translation .getValue (),
            rotation    = this ._rotation .getValue (),
            scale       = this ._scale .getValue (),
            center      = this ._center .getValue (),
            matrix4     = this .matrix;

         matrix3 .set ();

         if (!center .equals (Numbers_Vector2 .ZERO))
            matrix3 .translate (vector .assign (center) .negate ());

         if (!scale .equals (Numbers_Vector2 .ONE))
            matrix3 .scale (scale);

         if (rotation !== 0)
            matrix3 .rotate (rotation);

         if (!center .equals (Numbers_Vector2 .ZERO))
            matrix3 .translate (center);

         if (!translation .equals (Numbers_Vector2 .ZERO))
            matrix3 .translate (translation);

         matrix4 [ 0] = matrix3 [0];
         matrix4 [ 1] = matrix3 [1];
         matrix4 [ 4] = matrix3 [3];
         matrix4 [ 5] = matrix3 [4];
         matrix4 [12] = matrix3 [6];
         matrix4 [13] = matrix3 [7];

         this .setMatrix (matrix4);
      };
   })(),
});

Object .defineProperties (TextureTransform,
{
   ... Core_X3DNode .getStaticProperties ("TextureTransform", "Texturing", 1, "textureTransform", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mapping",     new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "translation", new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "rotation",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "scale",       new x_ite_Fields .SFVec2f (1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "center",      new x_ite_Fields .SFVec2f ()),
      ]),
      enumerable: true,
   },
});

const TextureTransform_default_ = TextureTransform;
;

/* harmony default export */ const Texturing_TextureTransform = (Namespace/* default */.A .add ("TextureTransform", TextureTransform_default_));
;// ./src/x_ite/Browser/Texturing/KTXDecoder.js
/* provided dependency */ var KTXDecoder_$ = __webpack_require__(44).A;
const KTXDecoder_default_ = class KTXDecoder
{
   constructor (gl, externalKtxlib, scriptDir)
   {
      this .gl          = gl;
      this .libktx      = null;
      this .initialized = this .init (gl, externalKtxlib, scriptDir);
   }

   async init (gl, externalKtxlib, scriptDir)
   {
      this .libktx = await externalKtxlib ({ preinitializedWebGLContext: gl }, scriptDir);

      this .libktx .GL .makeContextCurrent (this .libktx .GL .createContext (null, { majorVersion: gl .getVersion () }));
   }

   transcode (ktxTexture)
   {
      const { gl, libktx } = this;
      const { transcode_fmt } = libktx;

      let formatString;

      if (ktxTexture .needsTranscoding)
      {
         const
            astcSupported  = !!gl .getExtension ("WEBGL_compressed_texture_astc"),
            etcSupported   = !!gl .getExtension ("WEBGL_compressed_texture_etc1"),
            dxtSupported   = !!gl .getExtension ("WEBGL_compressed_texture_s3tc"),
            // bptcSupported  = !!gl .getExtension ("EXT_texture_compression_bptc"),
            pvrtcSupported = !!gl .getExtension ("WEBGL_compressed_texture_pvrtc") || !!gl .getExtension ("WEBKIT_WEBGL_compressed_texture_pvrtc");

         let format;

         if (astcSupported)
         {
            formatString = "ASTC";
            format       = transcode_fmt .ASTC_4x4_RGBA;
         }
         else if (dxtSupported)
         {
            formatString = ktxTexture.numComponents == 4 ? "BC3" : "BC1";
            format       = transcode_fmt .BC1_OR_3;
         }
         else if (pvrtcSupported)
         {
            formatString = "PVRTC1";
            format       = transcode_fmt .PVRTC1_4_RGBA;
         }
         else if (etcSupported)
         {
            formatString = "ETC";
            format       = transcode_fmt .ETC;
         }
         else
         {
            formatString = "RGBA4444";
            format       = transcode_fmt .RGBA4444;
         }

         if (ktxTexture .transcodeBasis (format, 0) != libktx .error_code .SUCCESS)
            throw new Error ("Texture transcode failed. See console for details.");
      }

      const result = ktxTexture .glUpload ();

      if (result .error != gl .NO_ERROR)
         throw new Error (`WebGL error when uploading texture, code = ${result .error .toString (16)}`);

      if (result .object === undefined)
         throw new Error ("Texture upload failed. See console for details.");

      return {
         target: result .target,
         object: result .object,
         format: formatString,
      };
   }

   async loadKTXFromURL (url, cache = true)
   {
      const response = await fetch (url, { cache: cache ? "default" : "reload" });

      if (!response .ok)
         throw new Error ("Couldn't fetch KTX image.");

      return this .loadKTXFromBuffer (await response .arrayBuffer ());
   }

   async loadKTXFromBuffer (arrayBuffer)
   {
      await this .initialized;

      const
         data         = new Uint8Array (await KTXDecoder_$.gunzip (arrayBuffer)),
         ktxTexture   = new this .libktx .ktxTexture (data),
         uploadResult = this .transcode (ktxTexture),
         texture      = uploadResult .object;

      texture .baseWidth     = ktxTexture .baseWidth;
      texture .baseHeight    = ktxTexture .baseHeight;
      texture .baseDepth     = ktxTexture .baseDepth ?? 1; // TODO: No support.
      texture .numComponents = ktxTexture .numComponents;
      texture .target        = uploadResult .target;

      ktxTexture .delete ();

      return texture;
   }
}
;

/* harmony default export */ const KTXDecoder = (Namespace/* default */.A .add ("KTXDecoder", KTXDecoder_default_));
;// ./src/x_ite/Browser/Texturing/X3DTexturingContext.js









const
   _maxTextures              = Symbol (),
   _numCombinedTextureUnits  = Symbol (),
   _globalTextureUnits       = Symbol (),
   _textureUnits             = Symbol (),
   _defaultTexture2DUnit     = Symbol (),
   _defaultTexture3DUnit     = Symbol (),
   _defaultTextureCubeUnit   = Symbol (),
   _defaultTexture2D         = Symbol (),
   _defaultTexture3D         = Symbol (),
   _defaultTextureCube       = Symbol (),
   _defaultTextureProperties = Symbol (),
   _defaultTextureTransform  = Symbol (),
   _defaultTextureCoordinate = Symbol (),
   _KTXDecoder               = Symbol ();

function X3DTexturingContext ()
{
   const
      gl                   = this .getContext (),
      maxTextureImageUnits = gl .getParameter (gl .MAX_TEXTURE_IMAGE_UNITS);

   // console .log (gl .getParameter (gl .MAX_TEXTURE_IMAGE_UNITS))
   // console .log (gl .getParameter (gl .MAX_ARRAY_TEXTURE_LAYERS))

   this [_maxTextures] = maxTextureImageUnits > 8 ? (/* inlined export .maxTextures */4) : (/* inlined export .maxTextures */4) / 2;
}

Object .assign (X3DTexturingContext .prototype,
{
   initialize ()
   {
      const gl = this .getContext ();

      gl .pixelStorei (gl .UNPACK_ALIGNMENT, 1);

      // Get texture Units

      let maxCombinedTextureUnits = gl .getParameter (gl .MAX_COMBINED_TEXTURE_IMAGE_UNITS);

      this [_defaultTexture2DUnit]    = -- maxCombinedTextureUnits;
      this [_defaultTexture3DUnit]    = -- maxCombinedTextureUnits;
      this [_defaultTextureCubeUnit]  = -- maxCombinedTextureUnits;
      this [_numCombinedTextureUnits] = maxCombinedTextureUnits;

      // Default Texture 2D Unit

      const defaultData = new Uint8Array ([ 255, 255, 255, 255 ]);

      this [_defaultTexture2D] = gl .createTexture ();

      gl .activeTexture (gl .TEXTURE0 + this [_defaultTexture2DUnit]);
      gl .bindTexture (gl .TEXTURE_2D, this [_defaultTexture2D]);
      gl .texImage2D (gl .TEXTURE_2D, 0, gl .RGBA, 1, 1, 0, gl .RGBA, gl .UNSIGNED_BYTE, defaultData);

      // Default Texture 3D Unit

      this [_defaultTexture3D] = gl .createTexture ();

      gl .activeTexture (gl .TEXTURE0 + this [_defaultTexture3DUnit]);
      gl .bindTexture (gl .TEXTURE_3D, this [_defaultTexture3D]);
      gl .texImage3D (gl .TEXTURE_3D, 0, gl .RGBA, 1, 1, 1, 0, gl .RGBA, gl .UNSIGNED_BYTE, defaultData);

      // Default Texture Cube Unit

      this [_defaultTextureCube] = gl .createTexture ();

      gl .activeTexture (gl .TEXTURE0 + this [_defaultTextureCubeUnit]);
      gl .bindTexture (gl .TEXTURE_CUBE_MAP, this [_defaultTextureCube]);

      const targets = [
         gl .TEXTURE_CUBE_MAP_POSITIVE_Z,
         gl .TEXTURE_CUBE_MAP_NEGATIVE_Z,
         gl .TEXTURE_CUBE_MAP_NEGATIVE_X,
         gl .TEXTURE_CUBE_MAP_POSITIVE_X,
         gl .TEXTURE_CUBE_MAP_POSITIVE_Y,
         gl .TEXTURE_CUBE_MAP_NEGATIVE_Y,
      ];

      for (const target of targets)
         gl .texImage2D (target, 0, gl .RGBA, 1, 1, 0, gl .RGBA, gl .UNSIGNED_BYTE, defaultData);

      // Reset texture units.

      gl .activeTexture (gl .TEXTURE0);

      this .resetGlobalTextureUnits ();

      // Set texture quality.

      this .setTextureQuality (this .getBrowserOptions () .getTextureQuality ());
   },
   getTextureMemory ()
   {
      return NaN;
   },
   getMaxTextures ()
   {
      return this [_maxTextures];
   },
   getMaxTextureTransforms ()
   {
      return (/* inlined export .maxTextureTransforms */4);
   },
   getMaxTexCoords ()
   {
      return (/* inlined export .maxTexCoords */4);
   },
   getMinTextureSize ()
   {
      return 16;
   },
   getMaxTextureSize ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_TEXTURE_SIZE);
   },
   getAnisotropicExtension: (() =>
   {
      // Anisotropic Filtering in WebGL is handled by an extension, use one of getExtension depending on browser:

      const ANISOTROPIC_EXT = [
         "EXT_texture_filter_anisotropic",
         "MOZ_EXT_texture_filter_anisotropic",
         "WEBKIT_EXT_texture_filter_anisotropic",
      ];

      return function ()
      {
         const gl = this .getContext ();

         for (const extension of ANISOTROPIC_EXT)
         {
            const ext = gl .getExtension (extension);

            if (ext)
               return ext;
         }
      };
   })(),
   getMaxAnisotropicDegree ()
   {
      const
         gl  = this .getContext (),
         ext = this .getAnisotropicExtension ();

      return ext ? gl .getParameter (ext .MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 1;
   },
   getMaxCombinedTextureUnits ()
   {
      const gl = this .getContext ();

      return gl .getParameter (gl .MAX_COMBINED_TEXTURE_IMAGE_UNITS)
   },
   popGlobalTextureUnit ()
   {
      return this [_textureUnits] = -- this [_globalTextureUnits];
   },
   resetGlobalTextureUnits ()
   {
      this [_textureUnits] = this [_globalTextureUnits] = this [_numCombinedTextureUnits];
   },
   popTextureUnit ()
   {
      return -- this [_textureUnits];
   },
   resetTextureUnits ()
   {
      this [_textureUnits] = this [_globalTextureUnits];
   },
   getDefaultTexture2DUnit ()
   {
      return this [_defaultTexture2DUnit];
   },
   getDefaultTexture3DUnit ()
   {
      return this [_defaultTexture3DUnit];
   },
   getDefaultTextureCubeUnit ()
   {
      return this [_defaultTextureCubeUnit];
   },
   getDefaultTexture2D ()
   {
      return this [_defaultTexture2D];
   },
   getDefaultTexture3D ()
   {
      return this [_defaultTexture3D];
   },
   getDefaultTextureCube ()
   {
      return this [_defaultTextureCube];
   },
   getDefaultTextureProperties ()
   {
      return this [_defaultTextureProperties] ??= (() =>
      {
         const defaultTextureProperties = new Texturing_TextureProperties (this .getPrivateScene ());

         defaultTextureProperties ._generateMipMaps     = true;
         defaultTextureProperties ._minificationFilter  = "NICEST";
         defaultTextureProperties ._magnificationFilter = "NICEST";

         defaultTextureProperties .setup ();

         return defaultTextureProperties;
      })();
   },
   getDefaultTextureTransform ()
   {
      return this [_defaultTextureTransform] ??= (() =>
      {
         const defaultTextureTransform = new Texturing_TextureTransform (this .getPrivateScene ());

         defaultTextureTransform .setup ();

         return defaultTextureTransform;
      })();
   },
   getDefaultTextureCoordinate ()
   {
      return this [_defaultTextureCoordinate] ??= (() =>
      {
         const defaultTextureCoordinate = new Texturing_TextureCoordinate (this .getPrivateScene ());

         defaultTextureCoordinate .setup ();

         return defaultTextureCoordinate;
      })();
   },
   setTextureQuality (textureQuality)
   {
      const
         gl                = this .getContext (),
         textureProperties = this .getDefaultTextureProperties ();

      switch (textureQuality)
      {
         case TextureQuality .LOW:
         {
            textureProperties ._magnificationFilter = "AVG_PIXEL";
            textureProperties ._minificationFilter  = "AVG_PIXEL";
            textureProperties ._textureCompression  = "FASTEST";
            textureProperties ._generateMipMaps     = true;

            gl .hint (gl .GENERATE_MIPMAP_HINT, gl .FASTEST);
            //gl .hint (gl .PERSPECTIVE_CORRECTION_HINT, gl .FASTEST);
            break;
         }
         case TextureQuality .MEDIUM:
         {
            textureProperties ._magnificationFilter = "NICEST";
            textureProperties ._minificationFilter  = "NEAREST_PIXEL_AVG_MIPMAP";
            textureProperties ._textureCompression  = "NICEST";
            textureProperties ._generateMipMaps     = true;

            gl .hint (gl .GENERATE_MIPMAP_HINT, gl .FASTEST);
            //gl .hint (gl .PERSPECTIVE_CORRECTION_HINT, gl .FASTEST);
            break;
         }
         case TextureQuality .HIGH:
         {
            textureProperties ._magnificationFilter = "NICEST";
            textureProperties ._minificationFilter  = "NICEST";
            textureProperties ._textureCompression  = "NICEST";
            textureProperties ._generateMipMaps     = true;

            gl .hint (gl .GENERATE_MIPMAP_HINT, gl .NICEST);
            //gl .hint (gl .PERSPECTIVE_CORRECTION_HINT, gl .NICEST);
            break;
         }
      }
   },
   async getKTXDecoder ()
   {
      return this [_KTXDecoder] ??= await (async () =>
      {
         const
            response = await fetch (Networking_URLs .getLibraryURL ("libktx.js")),
            text     = await response .text (),
            libKTX   = new Function (text) (),
            decoder  = new KTXDecoder (this .getContext (), libKTX, Networking_URLs .getLibraryURL ("libktx.js"));

         return decoder;
      })();
   },
});

const X3DTexturingContext_default_ = X3DTexturingContext;
;

/* harmony default export */ const Texturing_X3DTexturingContext = (Namespace/* default */.A .add ("X3DTexturingContext", X3DTexturingContext_default_));
;// ./src/x_ite/Browser/Time/X3DTimeContext.js


const
   _lastTime         = Symbol (),
   _currentTime      = Symbol (),
   _currentFrameRate = Symbol (),
   _currentPosition  = Symbol (),
   _currentSpeed     = Symbol ();

function X3DTimeContext ()
{
   this [_currentTime]      = Date .now () / 1000;
   this [_currentFrameRate] = 60;
   this [_currentPosition]  = new Numbers_Vector3 ();
   this [_currentSpeed]     = 0;
}

Object .assign (X3DTimeContext .prototype,
{
   getCurrentTime ()
   {
      return this [_currentTime];
   },
   getCurrentFrameRate ()
   {
      return this [_currentFrameRate];
   },
   getCurrentSpeed ()
   {
      return this [_currentSpeed];
   },
   advanceOnlyTime ()
   {
      this [_currentTime] = Date .now () / 1000;
   },
   advanceTime: (() =>
   {
      const lastPosition = new Numbers_Vector3 ();

      return function ()
      {
         const
            time          = Date .now () / 1000,
            interval      = time - this [_lastTime],
            viewpointNode = this .getActiveViewpoint ();

         if (interval <= 1 / Math .max (this .getBrowserOption ("MaximumFrameRate"), 1))
            return false;

         this [_lastTime]         = time;
         this [_currentTime]      = time;
         this [_currentFrameRate] = 1 / interval;

         if (viewpointNode)
         {
            const cameraSpaceMatrix = viewpointNode .getCameraSpaceMatrix ();

            lastPosition .assign (this [_currentPosition]);
            this [_currentPosition] .set (cameraSpaceMatrix [12], cameraSpaceMatrix [13], cameraSpaceMatrix [14]);

            this [_currentSpeed] = lastPosition .distance (this [_currentPosition]) * this [_currentFrameRate];
         }
         else
         {
            this [_currentSpeed] = 0;
         }

         if (this [_currentSpeed] > 0)
            this .addBrowserEvent ();

         return true;
      };
   })(),
});

const X3DTimeContext_default_ = X3DTimeContext;
;

/* harmony default export */ const Time_X3DTimeContext = (Namespace/* default */.A .add ("X3DTimeContext", X3DTimeContext_default_));
;// ./src/x_ite/Routing/X3DRoutingContext.js
const
   _taintedFields     = Symbol (),
   _taintedFieldsTemp = Symbol (),
   _taintedNodes      = Symbol (),
   _taintedNodesTemp  = Symbol (),
   _active            = Symbol ();

function X3DRoutingContext ()
{
   this [_taintedFields]     = [ ];
   this [_taintedFieldsTemp] = [ ];
   this [_taintedNodes]      = [ ];
   this [_taintedNodesTemp]  = [ ];
   this [_active]            = false;
}

Object .assign (X3DRoutingContext .prototype,
{
   initialize ()
   { },
   addTaintedField (field, event)
   {
      this [_taintedFields] .push (field, event);
   },
   addTaintedNode (node)
   {
      this [_taintedNodes] .push (node);
   },
   [Symbol .for ("X_ITE.X3DRoutingContext.processEvents")] ()
   {
      if (this [_active])
         return;

      this [_active] = true;

      do
      {
         // Process field events
         do
         {
            const taintedFields = this [_taintedFields];

            // Swap tainted fields.
            this [_taintedFields]         = this [_taintedFieldsTemp];
            this [_taintedFields] .length = 0;

            for (let i = 0, length = taintedFields .length; i < length; i += 2)
               taintedFields [i] .processEvent (taintedFields [i + 1]);

            // Don't know why this must be done after the for loop, otherwise a fatal error could be thrown.
            this [_taintedFieldsTemp] = taintedFields;
         }
         while (this [_taintedFields] .length);

         // Process node events
         do
         {
            const taintedNodes = this [_taintedNodes];

            // Swap tainted nodes.
            this [_taintedNodes]         = this [_taintedNodesTemp];
            this [_taintedNodes] .length = 0;

            for (const taintedNode of taintedNodes)
               taintedNode .processEvent ();

            // Don't know why this must be done after the for loop, otherwise a fatal error could be thrown.
            this [_taintedNodesTemp] = taintedNodes;
         }
         while (! this [_taintedFields] .length && this [_taintedNodes] .length);
      }
      while (this [_taintedFields] .length);

      this [_active] = false;
   },
   dispose ()
   { },
});

const X3DRoutingContext_default_ = X3DRoutingContext;
;

/* harmony default export */ const Routing_X3DRoutingContext = (Namespace/* default */.A .add ("X3DRoutingContext", X3DRoutingContext_default_));
;// ./src/x_ite/Configuration/AbstractNodesArray.js



function AbstractNodesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .typeName, value]), Function);
}

Object .assign (Object .setPrototypeOf (AbstractNodesArray .prototype, Base_X3DInfoArray .prototype),
{
   add (typeName, AbstractNode)
   {
      Base_X3DConstants .addConstant (AbstractNode .typeName);

      Base_X3DInfoArray .prototype .add .call (this, typeName, AbstractNode);
   },
});

for (const key of Object .keys (AbstractNodesArray .prototype))
   Object .defineProperty (AbstractNodesArray .prototype, key, { enumerable: false });

Object .defineProperties (AbstractNodesArray,
{
   typeName:
   {
      value: "AbstractNodesArray",
      enumerable: true,
   },
});

const AbstractNodesArray_default_ = AbstractNodesArray;
;

/* harmony default export */ const Configuration_AbstractNodesArray = (Namespace/* default */.A .add ("AbstractNodesArray", AbstractNodesArray_default_));
;// ./src/x_ite/Configuration/AbstractNodes.js


const AbstractNodes_default_ = new Configuration_AbstractNodesArray ();
;

/* harmony default export */ const AbstractNodes = (Namespace/* default */.A .add ("AbstractNodes", AbstractNodes_default_));
;// ./src/x_ite/Configuration/ConcreteNodesArray.js




function ConcreteNodesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .typeName, value]), Function);
}

Object .assign (Object .setPrototypeOf (ConcreteNodesArray .prototype, Base_X3DInfoArray .prototype),
{
   add (typeName, ConcreteNode)
   {
      Base_X3DConstants .addConstant (ConcreteNode .typeName);
      Parser_HTMLSupport .addConcreteNode (ConcreteNode);

      Base_X3DInfoArray .prototype .add .call (this, typeName, ConcreteNode);
   },
   update (oldTypeName, typeName, ConcreteNode)
   {
      Base_X3DConstants .addConstant (ConcreteNode .typeName);
      Parser_HTMLSupport .addConcreteNode (ConcreteNode);

      Base_X3DInfoArray .prototype .update .call (this, oldTypeName, typeName, ConcreteNode);
   },
});

for (const key of Object .keys (ConcreteNodesArray .prototype))
   Object .defineProperty (ConcreteNodesArray .prototype, key, { enumerable: false });

Object .defineProperties (ConcreteNodesArray,
{
   typeName:
   {
      value: "ConcreteNodesArray",
      enumerable: true,
   },
});

const ConcreteNodesArray_default_ = ConcreteNodesArray;
;

/* harmony default export */ const Configuration_ConcreteNodesArray = (Namespace/* default */.A .add ("ConcreteNodesArray", ConcreteNodesArray_default_));
;// ./src/x_ite/Configuration/ConcreteNodes.js


const ConcreteNodes_default_ = new Configuration_ConcreteNodesArray ();
;

/* harmony default export */ const ConcreteNodes = (Namespace/* default */.A .add ("ConcreteNodes", ConcreteNodes_default_));
;// ./src/x_ite/Browser/X3DBrowserContext.js




























const
   _world          = Symbol (),
   X3DBrowserContext_tainted        = Symbol (),
   _traverse       = Symbol .for ("X_ITE.X3DBrowserContext.traverse"),
   _renderCallback = Symbol (),
   _animFrame      = Symbol (),
   _previousTime   = Symbol (),
   _systemTime     = Symbol (),
   _browserTime    = Symbol (),
   _cameraTime     = Symbol (),
   _collisionTime  = Symbol (),
   _displayTime    = Symbol (),
   X3DBrowserContext_processEvents  = Symbol .for ("X_ITE.X3DRoutingContext.processEvents");

const
   browsers        = new Set (),
   browserContexts = [
      Routing_X3DRoutingContext,
      Scripting_X3DScriptingContext,
      Core_X3DCoreContext,
      Networking_X3DNetworkingContext,
      Texturing_X3DTexturingContext,
      Shaders_X3DShadersContext,
      Rendering_X3DRenderingContext,
      Shape_X3DShapeContext,
      Grouping_X3DGroupingContext,
      Geometry3D_X3DGeometry3DContext,
      PointingDeviceSensor_X3DPointingDeviceSensorContext,
      Navigation_X3DNavigationContext,
      Layering_X3DLayeringContext,
      EnvironmentalEffects_X3DEnvironmentalEffectsContext,
      Lighting_X3DLightingContext,
      Picking_X3DPickingContext,
      Sound_X3DSoundContext,
      Time_X3DTimeContext,
   ];

function X3DBrowserContext (element)
{
   browsers .add (this);

   Base_X3DBaseNode .call (this, this);

   for (const browserContext of browserContexts)
      browserContext .call (this, element);

   this .addChildObjects (Base_X3DConstants .outputOnly, "initialized",    new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "shutdown",       new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "prepareEvents",  new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "timeEvents",     new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "cameraEvents",   new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "sensorEvents",   new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "displayEvents",  new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "finishedEvents", new x_ite_Fields .SFTime (),
                          Base_X3DConstants .outputOnly, "endEvents",      new x_ite_Fields .SFTime ());

   this [X3DBrowserContext_tainted]        = false;
   this [_previousTime]   = 0;
   this [_renderCallback] = this [_traverse] .bind (this);
   this [_systemTime]     = new Time_StopWatch ();
   this [_browserTime]    = new Time_StopWatch ();
   this [_cameraTime]     = new Time_StopWatch ();
   this [_collisionTime]  = new Time_StopWatch ();
   this [_displayTime]    = new Time_StopWatch ();
};

Object .assign (Object .setPrototypeOf (X3DBrowserContext .prototype, Base_X3DBaseNode .prototype),
   ... browserContexts .map (browserContext => browserContext .prototype),
{
   initialize ()
   {
      Base_X3DBaseNode .prototype .initialize .call (this);

      for (const browserContext of browserContexts)
         browserContext .prototype .initialize ?.call (this);
   },
   initialized ()
   {
      return this ._initialized;
   },
   shutdown ()
   {
      return this ._shutdown;
   },
   prepareEvents ()
   {
      return this ._prepareEvents;
   },
   timeEvents ()
   {
      return this ._timeEvents;
   },
   cameraEvents ()
   {
      return this ._cameraEvents;
   },
   sensorEvents ()
   {
      return this ._sensorEvents;
   },
   displayEvents ()
   {
      return this ._displayEvents;
   },
   finishedEvents ()
   {
      return this ._finishedEvents;
   },
   endEvents ()
   {
      return this ._endEvents;
   },
   getBrowser ()
   {
      return this;
   },
   getWorld ()
   {
      return this [_world];
   },
   getExecutionContext ()
   {
      return this [_world] .getExecutionContext ();
   },
   setExecutionContext (executionContext)
   {
      this .getDefaultScene () .setExecutionContext (executionContext);

      this [_world] = new Execution_X3DWorld (executionContext);
      this [_world] .setup ();
      this [_world] .bindBindables ();
   },
   setSession (session)
   {
      this .getSession () .cancelAnimationFrame (this [_animFrame]);

      Rendering_X3DRenderingContext .prototype .setSession .call (this, session);

      this [X3DBrowserContext_tainted] = false;

      this .addBrowserEvent ();
   },
   addBrowserEvent ()
   {
      if (this [X3DBrowserContext_tainted])
         return;

      this [X3DBrowserContext_tainted]   = true;
      this [_animFrame] = this .getSession () .requestAnimationFrame (this [_renderCallback]);
   },
   nextFrame ()
   {
      return new Promise (resolve =>
      {
         const key = Symbol ();

         this .addBrowserEvent ();

         this ._endEvents .addFieldCallback (key, () =>
         {
            this ._endEvents .removeFieldCallback (key);
            resolve ();
         });
      });
   },
   [_traverse] (now, frame)
   {
      // Limit frame rate.

      this [X3DBrowserContext_tainted] = false;

      if (!this .advanceTime ())
      {
         this .addBrowserEvent ();
         return;
      }

      // Start

      this [_systemTime] .stop ();
      this [_browserTime] .start ();

      // Time

      this .xrFrame (frame);

      // Prepare and Time Events

      this .addTaintedField (this ._prepareEvents);
      this [X3DBrowserContext_processEvents] ();

      this .addTaintedField (this ._timeEvents);
      this [X3DBrowserContext_processEvents] ();

      // Camera

      this [_cameraTime] .start ();
      this [_world] .traverse (Rendering_TraverseType .CAMERA);
      this [_cameraTime] .stop ();

      this .addTaintedField (this ._cameraEvents);
      this [X3DBrowserContext_processEvents] ();

      // Collision

      this [_collisionTime] .start ();

      if (this .getCollisionCount () || this .getViewer () .isCollidable ())
         this [_world] .traverse (Rendering_TraverseType .COLLISION);

      this [_collisionTime] .stop ();

      // Sensor Events

      this .addTaintedField (this ._sensorEvents);
      this [X3DBrowserContext_processEvents] ();

      // Display

      this [_displayTime] .start ()
      this .addTaintedField (this ._displayEvents);
      this [X3DBrowserContext_processEvents] ();

      for (const frameBuffer of this .getFramebuffers ())
         frameBuffer .clear ();

      this [_world] .traverse (Rendering_TraverseType .DISPLAY);

      this .addTaintedField (this ._finishedEvents);
      this [X3DBrowserContext_processEvents] ();

      for (const frameBuffer of this .getFramebuffers ())
         frameBuffer .blit ();

      this [_displayTime] .stop ();

      this .addTaintedField (this ._endEvents);
      this [X3DBrowserContext_processEvents] ();

      // Finish

      this [_browserTime] .stop ();
      this [_systemTime] .start ();
   },
   getSystemTime ()
   {
      return this [_systemTime];
   },
   getBrowserTime ()
   {
      return this [_browserTime];
   },
   getCameraTime ()
   {
      return this [_cameraTime];
   },
   getCollisionTime ()
   {
      return this [_collisionTime];
   },
   getDisplayTime ()
   {
      return this [_displayTime];
   },
   dispose ()
   {
      browsers .delete (this);

      this [X3DBrowserContext_tainted] = true;

      this .getSession () .cancelAnimationFrame (this [_animFrame]);

      for (const browserContext of browserContexts .slice () .reverse ())
         browserContext .prototype .dispose ?.call (this);

      Base_X3DBaseNode .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DBrowserContext .prototype))
   Object .defineProperty (X3DBrowserContext .prototype, key, { enumerable: false });

Object .assign (X3DBrowserContext,
{
   addComponent ({ name, concreteNodes, abstractNodes, browserContext, external })
   {
      if (concreteNodes)
      {
         for (const ConcreteNode of concreteNodes)
            ConcreteNodes .add (ConcreteNode .typeName, ConcreteNode);
      }

      if (abstractNodes)
      {
         for (const AbstractNode of abstractNodes)
            AbstractNodes .add (AbstractNode .typeName, AbstractNode);
      }

      if (browserContext)
      {
         browserContexts .push (browserContext);

         const keys = Object .keys (browserContext .prototype)
            .filter (k => !k .match (/^(?:initialize|dispose)$/))
            .concat (Object .getOwnPropertySymbols (browserContext .prototype));

         for (const key of keys)
         {
            Object .defineProperty (X3DBrowserContext .prototype, key,
            {
               value: browserContext .prototype [key],
               writable: true,
            });
         }
      }

      for (const browser of browsers)
      {
         if (concreteNodes)
         {
            for (const ConcreteNode of concreteNodes)
               browser .addConcreteNode (ConcreteNode);
         }

         if (abstractNodes)
         {
            for (const AbstractNode of abstractNodes)
               browser .addAbstractNode (AbstractNode);
         }

         if (browserContext)
         {
            browserContext .call (browser);
            browserContext .prototype .initialize ?.call (browser);

            // Process events from context creation. This will setup nodes like
            // geometry option nodes before any node is created.
            browser [X3DBrowserContext_processEvents] ();
         }
      }

      if (external && DEVELOPMENT)
         console .info (`Done loading external component '${name}'.`);
   },
});

const X3DBrowserContext_default_ = X3DBrowserContext;
;

/* harmony default export */ const Browser_X3DBrowserContext = (Namespace/* default */.A .add ("X3DBrowserContext", X3DBrowserContext_default_));
;// ./src/x_ite/Browser/DOMIntegration.js
/* provided dependency */ var DOMIntegration_$ = __webpack_require__(44).A;
/*******************************************************************************
 * MIT License
 *
 * Copyright (c) 2016 Andreas Plesch
 * taken from https://github.com/andreasplesch/x_ite_dom.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 ******************************************************************************/






class DOMIntegration
{
   constructor (browser)
   {
      this .browser        = browser;
      this .rootElement    = undefined;
      this .canvasObserver = new MutationObserver (() => this .processCanvasMutation (browser));

      this .canvasObserver .observe (browser .getElement () [0],
      {
         attributes: false,
         childList: true,
      });

      this .processCanvasMutation (browser)
   }

   isPrivate ()
   {
      // Don't count as loading object.
      return true;
   }

   processCanvasMutation (browser)
   {
      this .processRootElement (browser, browser .getElement () .children ("X3D") .get (-1));
   }

   async processRootElement (browser, rootElement)
   {
      try
      {
         if (rootElement === this .rootElement)
            return;

         this .rootElement = rootElement;

         if (rootElement)
         {
            // Display splash screen.

            browser .setBrowserLoading (true);
            browser .addLoadingObject (this);

            // Now also attached node property to each node element.

            const scene = new Execution_X3DScene (browser);

            this .parser = new Parser_XMLParser (scene);

            this .parser .setInput (rootElement);

            await new Promise (this .parser .parseIntoScene .bind (this .parser));

            browser .replaceWorld (scene);

            // Create an observer instance.

            this .observer = new MutationObserver (mutations =>
            {
               for (const mutation of mutations)
                  this .processMutation (mutation);
            });

            // Start observing, also catches inlined Inline elements.

            this .observer .observe (rootElement,
            {
               attributes: true,
               childList: true,
               subtree: true,
            });

            // Add Inline elements from initial scene, and connect to node events.

            this .processInlineElements (rootElement);
            this .addEventDispatchersAll (rootElement);

            browser .removeLoadingObject (this);
         }
         else
         {
            browser .replaceWorld (null);
         }
      }
      catch (error)
      {
         console .error ("Error importing document:", error);
      }
   }

   processMutation (mutation)
   {
      switch (mutation .type)
      {
         case "attributes":
         {
            this .processAttribute (mutation);
            break;
         }
         case "childList":
         {
            for (const node of mutation .addedNodes)
               this .processAddedNode (node);

            for (const node of mutation .removedNodes)
               this .processRemovedNode (node);

            break;
         }
      }
   }

   processAttribute ({ target: element, attributeName })
   {
      const
         parser = this .parser,
         node   = DOMIntegration_$.data (element, "node");

      if (node)
      {
         const attribute = element .getAttributeNode (attributeName);

         parser .nodeAttribute (attribute, node);
      }
      else
      {
         // Is an attribute of non-node child such as fieldValue (or ROUTE).

         const
            parentNode = element .parentNode,
            node       = DOMIntegration_$.data (parentNode, "node");

         if (node)
         {
            parser .pushExecutionContext (node .getExecutionContext ());
            parser .pushParent (node);
            parser .childElement (element);
            parser .popParent ();
            parser .setupNodes ();
            parser .popExecutionContext ();
         }
      }
   }

   processAddedNode (element)
   {
      // Only process element nodes.

      if (element .nodeType !== Node .ELEMENT_NODE)
         return;

      if (element .nodeName === "X3D")
         return;

      if (DOMIntegration_$.data (element, "node"))
         return;

      const
         parentNode = element .parentNode,
         parser     = this .parser;

      if (parentNode .nodeName .match (/^(?:Scene|SCENE)$/))
      {
         // Root scene or Inline scene.

         const scene = DOMIntegration_$.data (parentNode, "node");

         parser .pushExecutionContext (scene);
         parser .childElement (element);
         parser .setupNodes ();
         parser .popExecutionContext ();
      }
      else if (DOMIntegration_$.data (parentNode, "node"))
      {
         // Use parent's scene if non-root, works for Inline.

         const
            node             = DOMIntegration_$.data (parentNode, "node"),
            executionContext = node .getExecutionContext ();

         parser .pushExecutionContext (executionContext);
         parser .pushParent (node);
         parser .childElement (element);
         parser .popParent ();
         parser .setupNodes ();
         parser .popExecutionContext ();
      }
      else
      {
         const scene = this .browser .currentScene;

         parser .pushExecutionContext (scene);
         parser .childElement (element);
         parser .setupNodes ();
         parser .popExecutionContext ();
      }

      // Now after creating nodes need to look again for Inline elements.

      this .processInlineElements (element);

      // Then attach event dispatchers.

      this .addEventDispatchers (element);
      this .addEventDispatchersAll (element);
   }

   processRemovedNode (element)
   {
      // Works also for root nodes, as it has to be, since scene .rootNodes is effectively a MFNode in x-ite.
      // Also removes ROUTE elements.

      const node = DOMIntegration_$.data (element, "node");

      if (!node)
         return;

      node .dispose ();

      DOMIntegration_$.data (element, "node", null);
   }

   processInlineElements (element)
   {
      if (element .nodeName .match (/^(?:Inline|INLINE)$/))
         this .processInlineElement (element);

      for (const inlineElement of element .querySelectorAll ("Inline"))
         this .processInlineElement (inlineElement);
   }

   processInlineElement (element)
   {
      const node = DOMIntegration_$.data (element, "node");

      if (!node)
         return;

      node ._loadState .addInterest ("appendInlineChildElement", this, element);

      this .appendInlineChildElement (element);
   }

   appendInlineChildElement (element)
   {
      const node = DOMIntegration_$.data (element, "node");

      switch (node .checkLoadState ())
      {
         case Base_X3DConstants .NOT_STARTED_STATE:
         case Base_X3DConstants .FAILED_STATE:
         {
            // Remove all child nodes.

            while (element .firstChild)
               element .removeChild (element .lastChild);

            break;
         }
         case Base_X3DConstants .COMPLETE_STATE:
         {
            // Remove all child nodes.

            while (element .firstChild)
               element .removeChild (element .lastChild);

            // Add scene as child node of Inline element.

            const X3DElement = DOMIntegration_$.data (node .getInternalScene (), "X3D");

            if (!X3DElement)
               break;

            element .appendChild (X3DElement);

            // Add Inline elements, and connect to node events.

            this .processInlineElements (X3DElement);
            this .addEventDispatchersAll (X3DElement);

            break;
         }
      }

      switch (node .checkLoadState ())
      {
         case Base_X3DConstants .COMPLETE_STATE:
         {
            const event = new CustomEvent ("load",
            {
               detail: { node: Fields_SFNodeCache .get (node) },
            });

            element .dispatchEvent (event);
            break;
         }
         case Base_X3DConstants .FAILED_STATE:
         {
            const event = new CustomEvent ("error",
            {
               detail: { node: Fields_SFNodeCache .get (node) },
            });

            element .dispatchEvent (event);
            break;
         }
      }
   }

   addEventDispatchersAll (element)
   {
      const childElements = element .querySelectorAll ("*");

      for (const childElement of childElements)
         this .addEventDispatchers (childElement);
   }

   addEventDispatchers (element)
   {
      // Check for USE nodes; they do not emit events.

      if (element .nodeName === "ROUTE")
         return;

      const node = DOMIntegration_$.data (element, "node");

      if (!node)
         return;

      for (const field of node .getPredefinedFields ())
         this .bindFieldCallback (field, element);

      for (const field of node .getUserDefinedFields ())
         this .bindFieldCallback (field, element);
   }

   bindFieldCallback (field, element)
   {
      if (!field .isOutput ())
         return;

      field .addInterest ("fieldCallback", this, element);
   }

   fieldCallback (element, field)
   {
      const node = DOMIntegration_$.data (element, "node");

      if (!node)
         return;

      // Unfortunately `getEventListeners` is not available all the time.
      // if (!getEventListeners (element) .hasOwnProperty (field .getName ()))
      //    return;

      const event = new CustomEvent (field .getName (),
      {
         detail: {
            node: Fields_SFNodeCache .get (node),
            value: field .valueOf (),
         },
      });

      element .dispatchEvent (event);
   }

   dispose ()
   {
      this .canvasObserver .disconnect ();
   }
};

const DOMIntegration_default_ = DOMIntegration;
;

/* harmony default export */ const Browser_DOMIntegration = (Namespace/* default */.A .add ("DOMIntegration", DOMIntegration_default_));
;// ./src/x_ite/Configuration/ProfileInfo.js



function ProfileInfo (name, title, providerURL, components)
{
   Object .defineProperties (this,
   {
      name: { value: name, enumerable: true },
      title: { value: title, enumerable: true },
      providerURL: { value: providerURL || Networking_URLs .getProviderURL (), enumerable: true },
      components: { value: components, enumerable: true },
   });
}

Object .assign (Object .setPrototypeOf (ProfileInfo .prototype, Base_X3DObject .prototype),
{
   toVRMLStream (generator)
   {
      generator .Indent ();
      generator .string += "PROFILE";
      generator .Space ();
      generator .string += this .name;
   },
   toXMLStream (generator)
   {
      generator .string += this .name;
   },
   toJSONStream (generator)
   {
      generator .string += this .name;
   },
});

for (const key of Object .keys (ProfileInfo .prototype))
   Object .defineProperty (ProfileInfo .prototype, key, { enumerable: false });

Object .defineProperties (ProfileInfo,
{
   typeName:
   {
      value: "ProfileInfo",
      enumerable: true,
   },
});

Object .defineProperties (ProfileInfo .prototype,
{
   providerUrl: // legacy
   {
      get: function () { return this .providerURL; },
   },
});

const ProfileInfo_default_ = ProfileInfo;
;

/* harmony default export */ const Configuration_ProfileInfo = (Namespace/* default */.A .add ("ProfileInfo", ProfileInfo_default_));
;// ./src/x_ite/Configuration/ProfileInfoArray.js




function ProfileInfoArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .name, value]), Configuration_ProfileInfo);
}

Object .assign (Object .setPrototypeOf (ProfileInfoArray .prototype, Base_X3DInfoArray .prototype),
{
   add (name, { title, providerURL, components })
   {
      Base_X3DInfoArray .prototype .add .call (this, name, new Configuration_ProfileInfo (name, title, providerURL, new Configuration_ComponentInfoArray (components)));
   },
});

for (const key of Object .keys (ProfileInfoArray .prototype))
   Object .defineProperty (ProfileInfoArray .prototype, key, { enumerable: false });

Object .defineProperties (ProfileInfoArray,
{
   typeName:
   {
      value: "ProfileInfoArray",
      enumerable: true,
   },
});

const ProfileInfoArray_default_ = ProfileInfoArray;
;

/* harmony default export */ const Configuration_ProfileInfoArray = (Namespace/* default */.A .add ("ProfileInfoArray", ProfileInfoArray_default_));
;// ./src/x_ite/Configuration/SupportedComponents.js


const SupportedComponents = new Configuration_ComponentInfoArray ([ ]);

// SupportedComponents .add ("Annotation",
// {
//    title: "Annotation",
//    level: 2,
//    external: true,
// });

SupportedComponents .add ("CADGeometry",
{
   title: "CAD geometry",
   level: 2,
   external: true,
});

SupportedComponents .add ("Core",
{
   title: "Core",
   level: 2,
});

SupportedComponents .add ("CubeMapTexturing",
{
   title: "Cube map environmental texturing",
   level: 3,
   external: true,
});

SupportedComponents .add ("DIS",
{
   title: "Distributed interactive simulation",
   level: 2,
   external: true,
});

SupportedComponents .add ("EnvironmentalEffects",
{
   title: "Environmental effects",
   level: 4,
});

SupportedComponents .add ("EnvironmentalSensor",
{
   title: "Environmental sensor",
   level: 3,
});

SupportedComponents .add ("EventUtilities",
{
   title: "Event utilities",
   level: 1,
   external: true,
});

SupportedComponents .add ("Followers",
{
   title: "Followers",
   level: 1,
});

SupportedComponents .add ("GaussianSplats",
{
   title: "Gaussian splats",
   level: 1,
   external: true,
});

SupportedComponents .add ("Geometry2D",
{
   title: "Geometry2D",
   level: 2,
   external: true,
});

SupportedComponents .add ("Geometry3D",
{
   title: "Geometry3D",
   level: 4,
});

SupportedComponents .add ("Geospatial",
{
   title: "Geospatial",
   level: 2,
   external: true,
});

SupportedComponents .add ("Grouping",
{
   title: "Grouping",
   level: 3,
});

SupportedComponents .add ("HAnim",
{
   title: "Humanoid animation (HAnim)",
   level: 3,
   external: true,
});

SupportedComponents .alias ("H-Anim", SupportedComponents .get ("HAnim"));

SupportedComponents .add ("Interpolation",
{
   title: "Interpolation",
   level: 5,
});

SupportedComponents .add ("KeyDeviceSensor",
{
   title: "Key device sensor",
   level: 2,
   external: true,
});

SupportedComponents .add ("Layering",
{
   title: "Layering",
   level: 1,
});

SupportedComponents .add ("Layout",
{
   title: "Layout",
   level: 2,
   external: true,
   dependencies: ["Text"],
});

SupportedComponents .add ("Lighting",
{
   title: "Lighting",
   level: 3,
});

SupportedComponents .add ("Navigation",
{
   title: "Navigation",
   level: 3,
});

SupportedComponents .add ("Networking",
{
   title: "Networking",
   level: 4,
});

SupportedComponents .add ("NURBS",
{
   title: "NURBS",
   level: 4,
   external: true,
});

SupportedComponents .add ("ParticleSystems",
{
   title: "Particle systems",
   level: 3,
   external: true,
});

SupportedComponents .add ("Picking",
{
   title: "Picking",
   level: 3,
   external: true,
   dependencies: ["RigidBodyPhysics"],
});

SupportedComponents .add ("PointingDeviceSensor",
{
   title: "Pointing device sensor",
   level: 1,
});

SupportedComponents .add ("Shaders",
{
   title: "Programmable shaders",
   level: 1,
});

SupportedComponents .add ("TextureProjection",
{
   title: "Texture Projection",
   level: 2,
   external: true,
});

SupportedComponents .alias ("TextureProjector",         SupportedComponents .get ("TextureProjection"));
SupportedComponents .alias ("ProjectiveTextureMapping", SupportedComponents .get ("TextureProjection"));

SupportedComponents .add ("Rendering",
{
   title: "Rendering",
   level: 5,
});

SupportedComponents .add ("RigidBodyPhysics",
{
   title: "Rigid body physics",
   level: 2,
   external: true,
});

SupportedComponents .add ("Scripting",
{
   title: "Scripting",
   level: 1,
   external: true,
});

SupportedComponents .add ("Shape",
{
   title: "Shape",
   level: 4,
});

SupportedComponents .add ("Sound",
{
   title: "Sound",
   level: 3,
});

SupportedComponents .add ("Text",
{
   title: "Text",
   level: 2,
   external: true,
});

SupportedComponents .add ("Texturing",
{
   title: "Texturing",
   level: 4,
});

SupportedComponents .add ("Texturing3D",
{
   title: "Texturing3D",
   level: 2,
   external: true,
});

SupportedComponents .add ("Time",
{
   title: "Time",
   level: 2,
});

SupportedComponents .add ("VolumeRendering",
{
   title: "Volume rendering",
   level: 4,
   external: true,
   dependencies: ["CADGeometry", "Texturing3D"],
});

SupportedComponents .add ("WebXR",
{
   title: "WebXR",
   level: 1,
   external: true,
});

SupportedComponents .add ("X_ITE",
{
   title: "X_ITE",
   level: 1,
   external: true,
});

const SupportedComponents_default_ = SupportedComponents;
;

/* harmony default export */ const Configuration_SupportedComponents = (Namespace/* default */.A .add ("SupportedComponents", SupportedComponents_default_));
;// ./src/x_ite/Configuration/SupportedProfiles.js





function getComponent (name, level = 0)
{
   const component = Configuration_SupportedComponents .get (name);

   if (DEVELOPMENT)
   {
      if (level < 1 || level > component .level)
         throw new Error (`Component ${name} level ${level} exceeds maximum level ${component .level}.`);
   }

   return new Configuration_ComponentInfo (component .name,
      level,
      component .title,
      component .providerURL,
      component .external,
      component .dependencies);
}

const SupportedProfiles = new Configuration_ProfileInfoArray ();

SupportedProfiles .add ("CADInterchange",
{
   title: "Computer-Aided Design (CAD) interchange",
   components: [
      getComponent ("CADGeometry", 2),
      getComponent ("Core", 1),
      getComponent ("Grouping", 1),
      getComponent ("Lighting", 1),
      getComponent ("Navigation", 3),
      getComponent ("Networking", 2),
      getComponent ("Rendering", 4),
      getComponent ("Shaders", 1),
      getComponent ("Shape", 2),
      getComponent ("Texturing", 2),
   ],
});

SupportedProfiles .add ("Core",
{
   title: "Core",
   components: [
      getComponent ("Core", 1),
   ],
});

SupportedProfiles .add ("Full",
{
   title: "Full",
   components: [
      //getComponent ("Annotation", 1),
      getComponent ("CADGeometry", 2),
      getComponent ("Core", 2),
      getComponent ("CubeMapTexturing", 3),
      getComponent ("DIS", 2),
      getComponent ("EnvironmentalEffects", 4),
      getComponent ("EnvironmentalSensor", 3),
      getComponent ("EventUtilities", 1),
      getComponent ("Followers", 1),
      getComponent ("GaussianSplats", 1),
      getComponent ("Geometry2D", 2),
      getComponent ("Geometry3D", 4),
      getComponent ("Geospatial", 2),
      getComponent ("Grouping", 3),
      getComponent ("HAnim", 3),
      getComponent ("Interpolation", 5),
      getComponent ("KeyDeviceSensor", 2),
      getComponent ("Layering", 1),
      getComponent ("Layout", 2),
      getComponent ("Lighting", 3),
      getComponent ("Navigation", 3),
      getComponent ("Networking", 4),
      getComponent ("NURBS", 4),
      getComponent ("ParticleSystems", 3),
      getComponent ("Picking", 3),
      getComponent ("PointingDeviceSensor", 1),
      getComponent ("Rendering", 5),
      getComponent ("RigidBodyPhysics", 2),
      getComponent ("Scripting", 1),
      getComponent ("Shaders", 1),
      getComponent ("Shape", 4),
      getComponent ("Sound", 3),
      getComponent ("Text", 2),
      getComponent ("TextureProjection", 2),
      getComponent ("Texturing", 4),
      getComponent ("Texturing3D", 2),
      getComponent ("Time", 2),
      getComponent ("VolumeRendering", 4),
   ],
});

SupportedProfiles .add ("Immersive",
{
   title: "Immersive",
   components: [
      getComponent ("Core", 2),
      getComponent ("EnvironmentalEffects", 2),
      getComponent ("EnvironmentalSensor", 2),
      getComponent ("EventUtilities", 1),
      getComponent ("Geometry2D", 1),
      getComponent ("Geometry3D", 4),
      getComponent ("Grouping", 2),
      getComponent ("Interpolation", 2),
      getComponent ("KeyDeviceSensor", 2),
      getComponent ("Lighting", 2),
      getComponent ("Navigation", 2),
      getComponent ("Networking", 3),
      getComponent ("PointingDeviceSensor", 1),
      getComponent ("Rendering", 3),
      getComponent ("Scripting", 1),
      getComponent ("Shape", 2),
      getComponent ("Sound", 1),
      getComponent ("Text", 2),
      getComponent ("Texturing", 3),
      getComponent ("Time", 1),
   ],
});

SupportedProfiles .add ("Interactive",
{
   title: "Interactive",
   components: [
      getComponent ("Core", 1),
      getComponent ("EnvironmentalEffects", 1),
      getComponent ("EnvironmentalSensor", 1),
      getComponent ("EventUtilities", 1),
      getComponent ("Geometry3D", 3),
      getComponent ("Grouping", 2),
      getComponent ("Interpolation", 2),
      getComponent ("KeyDeviceSensor", 1),
      getComponent ("Lighting", 2),
      getComponent ("Navigation", 1),
      getComponent ("Networking", 2),
      getComponent ("PointingDeviceSensor", 1),
      getComponent ("Rendering", 3),
      getComponent ("Shape", 1),
      getComponent ("Texturing", 2),
      getComponent ("Time", 1),
   ],
});

SupportedProfiles .add ("Interchange",
{
   title: "Interchange",
   components: [
      getComponent ("Core", 1),
      getComponent ("EnvironmentalEffects", 1),
      getComponent ("Geometry3D", 2),
      getComponent ("Grouping", 1),
      getComponent ("Interpolation", 2),
      getComponent ("Lighting", 1),
      getComponent ("Navigation", 1),
      getComponent ("Networking", 1),
      getComponent ("Rendering", 3),
      getComponent ("Shape", 1),
      getComponent ("Texturing", 2),
      getComponent ("Time", 1),
   ],
});

SupportedProfiles .add ("MedicalInterchange",
{
   title: "Medical interchange",
   components: [
      getComponent ("Core", 1),
      getComponent ("EnvironmentalEffects", 1),
      getComponent ("EventUtilities", 1),
      getComponent ("Geometry2D", 2),
      getComponent ("Geometry3D", 2),
      getComponent ("Grouping", 3),
      getComponent ("Interpolation", 2),
      getComponent ("Lighting", 1),
      getComponent ("Navigation", 3),
      getComponent ("Networking", 2),
      getComponent ("Rendering", 5),
      getComponent ("Shape", 3),
      getComponent ("Text", 2),
      getComponent ("Texturing", 2),
      getComponent ("Texturing3D", 2),
      getComponent ("Time", 1),
      getComponent ("VolumeRendering", 4),
   ],
});

SupportedProfiles .add ("MPEG4Interactive",
{
   title: "MPEG-4 interactive",
   components: [
      getComponent ("Core", 1),
      getComponent ("EnvironmentalEffects", 1),
      getComponent ("EnvironmentalSensor", 1),
      getComponent ("Geometry3D", 2),
      getComponent ("Grouping", 2),
      getComponent ("Interpolation", 2),
      getComponent ("Lighting", 2),
      getComponent ("Navigation", 1),
      getComponent ("Networking", 2),
      getComponent ("PointingDeviceSensor", 1),
      getComponent ("Rendering", 1),
      getComponent ("Shape", 1),
      getComponent ("Texturing", 1),
      getComponent ("Time", 1),
   ],
});

SupportedProfiles .alias ("MPEG-4", SupportedProfiles .get ("MPEG4Interactive"));

const SupportedProfiles_default_ = SupportedProfiles;
;

/* harmony default export */ const Configuration_SupportedProfiles = (Namespace/* default */.A .add ("SupportedProfiles", SupportedProfiles_default_));
;// ./src/x_ite/Configuration/FieldTypesArray.js


function FieldTypesArray (values = [ ])
{
   return Base_X3DInfoArray .call (this, Array .from (values, value => [value .typeName, value]), Function);
}

Object .setPrototypeOf (FieldTypesArray .prototype, Base_X3DInfoArray .prototype);

for (const key of Object .keys (FieldTypesArray .prototype))
   Object .defineProperty (FieldTypesArray .prototype, key, { enumerable: false });

Object .defineProperties (FieldTypesArray,
{
   typeName:
   {
      value: "FieldTypesArray",
      enumerable: true,
   },
});

const FieldTypesArray_default_ = FieldTypesArray;
;

/* harmony default export */ const Configuration_FieldTypesArray = (Namespace/* default */.A .add ("FieldTypesArray", FieldTypesArray_default_));
;// ./src/x_ite/Configuration/FieldTypes.js



const FieldTypes_default_ = new Configuration_FieldTypesArray (Object .values (x_ite_Fields));
;

/* harmony default export */ const FieldTypes = (Namespace/* default */.A .add ("FieldTypes", FieldTypes_default_));
;// ./src/x_ite/Components/Core/X3DMetadataObject.js



function X3DMetadataObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DMetadataObject);
}

Object .assign (X3DMetadataObject .prototype,
{
   initialize ()
   { },
   dispose ()
   { },
});

Object .defineProperties (X3DMetadataObject, Core_X3DNode .getStaticProperties ("X3DMetadataObject", "Core", 1));

const X3DMetadataObject_default_ = X3DMetadataObject;
;

/* harmony default export */ const Core_X3DMetadataObject = (Namespace/* default */.A .add ("X3DMetadataObject", X3DMetadataObject_default_));
;// ./src/x_ite/Components/Core/MetadataBoolean.js








function MetadataBoolean (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataBoolean);
}

Object .assign (Object .setPrototypeOf (MetadataBoolean .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataBoolean,
{
   ... Core_X3DNode .getStaticProperties ("MetadataBoolean", "Core", 1, "value", "3.3"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFBool ()),
      ]),
      enumerable: true,
   },
});

const MetadataBoolean_default_ = MetadataBoolean;
;

/* harmony default export */ const Core_MetadataBoolean = (Namespace/* default */.A .add ("MetadataBoolean", MetadataBoolean_default_));
;// ./src/x_ite/Components/Core/MetadataDouble.js








function MetadataDouble (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataDouble);
}

Object .assign (Object .setPrototypeOf (MetadataDouble .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataDouble,
{
   ... Core_X3DNode .getStaticProperties ("MetadataDouble", "Core", 1, "value", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFDouble ()),
      ]),
      enumerable: true,
   },
});

const MetadataDouble_default_ = MetadataDouble;
;

/* harmony default export */ const Core_MetadataDouble = (Namespace/* default */.A .add ("MetadataDouble", MetadataDouble_default_));
;// ./src/x_ite/Components/Core/MetadataFloat.js








function MetadataFloat (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataFloat);
}

Object .assign (Object .setPrototypeOf (MetadataFloat .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataFloat,
{
   ... Core_X3DNode .getStaticProperties ("MetadataFloat", "Core", 1, "value", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const MetadataFloat_default_ = MetadataFloat;
;

/* harmony default export */ const Core_MetadataFloat = (Namespace/* default */.A .add ("MetadataFloat", MetadataFloat_default_));
;// ./src/x_ite/Components/Core/MetadataInteger.js








function MetadataInteger (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataInteger);
}

Object .assign (Object .setPrototypeOf (MetadataInteger .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataInteger,
{
   ... Core_X3DNode .getStaticProperties ("MetadataInteger", "Core", 1, "value", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFInt32 ()),
      ]),
      enumerable: true,
   },
});

const MetadataInteger_default_ = MetadataInteger;
;

/* harmony default export */ const Core_MetadataInteger = (Namespace/* default */.A .add ("MetadataInteger", MetadataInteger_default_));
;// ./src/x_ite/Components/Core/MetadataSet.js








function MetadataSet (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataSet);
}

Object .assign (Object .setPrototypeOf (MetadataSet .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   getMetaValue (name)
   {
      return this ._value .find (node => node .name === name);
   },
   setMetaValue (name, value)
   {
      if (!Array .isArray (value))
         value = [value .valueOf ()];

      switch (value .type ?? (typeof value [0]))
      {
         case "boolean":
         {
            this .getMetadataObject ("MetadataBoolean", name, true) .value = value;
            return;
         }
         case "number":
         case "double":
         {
            this .getMetadataObject ("MetadataDouble", name, true) .value = value;
            break;
         }
         case "float":
         {
            this .getMetadataObject ("MetadataFloat", name, true) .value = value;
            break;
         }
         case "integer":
         {
            this .getMetadataObject ("MetadataInteger", name, true) .value = value;
            break;
         }
         case "string":
         {
            this .getMetadataObject ("MetadataString", name, true) .value = value;
            return;
         }
      }
   },
   removeMetaValue (name)
   {
      const index = this ._value .findIndex (node => node .name === name);

      if (index < 0)
         return;

      this ._value .splice (index, 1);
   },
   getMetadataObject (typeName, name, create = false)
   {
      let metadata = this .getMetaValue (name);

      if (metadata ?.getNodeTypeName () === typeName)
         return metadata;

      if (!create)
         return null;

      if (metadata)
         this .removeMetaValue (name);

      metadata = this .getExecutionContext () .createNode (typeName);

      this .getExecutionContext () .addNamedNode (this .getExecutionContext () .getUniqueName (name), metadata);

      metadata .reference = this .getBrowser () .getBrowserOption ("MetadataReference");
      metadata .name      = name;

      this ._value .push (metadata);

      return metadata;
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataSet,
{
   ... Core_X3DNode .getStaticProperties ("MetadataSet", "Core", 1, "value", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const MetadataSet_default_ = MetadataSet;
;

/* harmony default export */ const Core_MetadataSet = (Namespace/* default */.A .add ("MetadataSet", MetadataSet_default_));
;// ./src/x_ite/Components/Core/MetadataString.js








function MetadataString (executionContext)
{
   Core_X3DNode           .call (this, executionContext);
   Core_X3DMetadataObject .call (this, executionContext);

   this .addType (Base_X3DConstants .MetadataString);
}

Object .assign (Object .setPrototypeOf (MetadataString .prototype, Core_X3DNode .prototype),
   Core_X3DMetadataObject .prototype,
{
   initialize ()
   {
      Core_X3DNode           .prototype .initialize .call (this);
      Core_X3DMetadataObject .prototype .initialize .call (this);
   },
   getContainerField (specificationVersion = LATEST_VERSION)
   {
      if (specificationVersion <= 3.3)
         return "metadata";

      return Core_X3DNode .prototype .getContainerField .call (this);
   },
   dispose ()
   {
      Core_X3DMetadataObject .prototype .dispose .call (this);
      Core_X3DNode           .prototype .dispose .call (this);
   },
});

Object .defineProperties (MetadataString,
{
   ... Core_X3DNode .getStaticProperties ("MetadataString", "Core", 1, "value", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "name",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "reference", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "value",     new x_ite_Fields .MFString ()),
      ]),
      enumerable: true,
   },
});

const MetadataString_default_ = MetadataString;
;

/* harmony default export */ const Core_MetadataString = (Namespace/* default */.A .add ("MetadataString", MetadataString_default_));
;// ./src/x_ite/Components/Core/X3DInfoNode.js




function X3DInfoNode (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DInfoNode);
}

Object .setPrototypeOf (X3DInfoNode .prototype, Core_X3DChildNode .prototype);

Object .defineProperties (X3DInfoNode, Core_X3DNode .getStaticProperties ("X3DInfoNode", "Core", 1));

const X3DInfoNode_default_ = X3DInfoNode;
;

/* harmony default export */ const Core_X3DInfoNode = (Namespace/* default */.A .add ("X3DInfoNode", X3DInfoNode_default_));
;// ./src/x_ite/Components/Core/WorldInfo.js







function WorldInfo (executionContext)
{
   Core_X3DInfoNode .call (this, executionContext);

   this .addType (Base_X3DConstants .WorldInfo);
}

Object .assign (Object .setPrototypeOf (WorldInfo .prototype, Core_X3DInfoNode .prototype),
{
   initialize ()
   {
      Core_X3DInfoNode .prototype .initialize .call (this);

      if (this .isLive ())
         this .getExecutionContext () .addWorldInfo (this);
   },
   setLive (value)
   {
      if (!!value .valueOf () === this .isLive ())
         return;

      Core_X3DInfoNode .prototype .setLive .call (this, value);

      if (this .isLive ())
         this .getExecutionContext () .addWorldInfo (this);
      else
         this .getExecutionContext () .removeWorldInfo (this);
   },
});

Object .defineProperties (WorldInfo,
{
   ... Core_X3DNode .getStaticProperties ("WorldInfo", "Core", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "title",    new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "info",     new x_ite_Fields .MFString ()),
      ]),
      enumerable: true,
   },
});

const WorldInfo_default_ = WorldInfo;
;

/* harmony default export */ const Core_WorldInfo = (Namespace/* default */.A .add ("WorldInfo", WorldInfo_default_));
;// ./src/x_ite/Components/CoreComponent.js
















const CoreComponent_default_ = {
   name: "Core",
   concreteNodes:
   [
      Core_MetadataBoolean,
      Core_MetadataDouble,
      Core_MetadataFloat,
      Core_MetadataInteger,
      Core_MetadataSet,
      Core_MetadataString,
      Core_WorldInfo,
   ],
   abstractNodes:
   [
      Core_X3DBindableNode,
      Core_X3DChildNode,
      Core_X3DImportedNodeProxy,
      Core_X3DInfoNode,
      Core_X3DMetadataObject,
      Core_X3DNode,
      Core_X3DPrototypeInstance,
      Core_X3DSensorNode,
   ],
};
;

/* harmony default export */ const CoreComponent = (Namespace/* default */.A .add ("CoreComponent", CoreComponent_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/FogCoordinate.js







function FogCoordinate (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .FogCoordinate);

   // Units

   this ._depth .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (FogCoordinate .prototype, Rendering_X3DGeometricPropertyNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometricPropertyNode .prototype .initialize .call (this);

      this ._depth .addInterest ("set_depth__", this);

      this .set_depth__ ();
   },
   set_depth__ ()
   {
      this .depth  = this ._depth .getValue ();
      this .length = this ._depth .length;
   },
   getSize ()
   {
      return this .length;
   },
   addDepth (index, array)
   {
      if (index >= 0 && this .length)
      {
         return array .push (this .depth [index % this .length]);
      }
      else
      {
         array .push (0);
      }
   },
   addDepths (array, min = this .length)
   {
      const length = this .length;

      if (length)
      {
         const depth = this .depth;

         for (let index = 0; index < min; ++ index)
            array .push (depth [index % length]);
      }
      else
      {
         for (let index = 0; index < min; ++ index)
            array .push (0);
      }

      return array;
   },
});

Object .defineProperties (FogCoordinate,
{
   ... Core_X3DNode .getStaticProperties ("FogCoordinate", "EnvironmentalEffects", 4, "fogCoord", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "depth",    new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const FogCoordinate_default_ = FogCoordinate;
;

/* harmony default export */ const EnvironmentalEffects_FogCoordinate = (Namespace/* default */.A .add ("FogCoordinate", FogCoordinate_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/LocalFog.js








function LocalFog (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);
   EnvironmentalEffects_X3DFogObject .call (this, executionContext);

   this .addType (Base_X3DConstants .LocalFog);
}

Object .assign (Object .setPrototypeOf (LocalFog .prototype, Core_X3DChildNode .prototype),
   EnvironmentalEffects_X3DFogObject .prototype,
{
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);
      EnvironmentalEffects_X3DFogObject .prototype .initialize .call (this);
   },
   push (renderObject)
   {
      if (this ._enabled .getValue () && this .getFogType ())
      {
         const fogContainer = this .getFogs () .pop ();

         fogContainer .set (this, renderObject .getModelViewMatrix () .get ());

         renderObject .getLocalFogs () .push (fogContainer);
      }
   },
   pop (renderObject)
   {
      if (this ._enabled .getValue () && this .getFogType ())
         this .getBrowser () .getLocalObjects () .add (renderObject .getLocalFogs () .pop ());
   },
   dispose ()
   {
      EnvironmentalEffects_X3DFogObject .prototype .dispose .call (this);
      Core_X3DChildNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (LocalFog,
{
   ... Core_X3DNode .getStaticProperties ("LocalFog", "EnvironmentalEffects", 4, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fogType",         new x_ite_Fields .SFString ("LINEAR")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",           new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visibilityStart", new x_ite_Fields .SFFloat ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "visibilityRange", new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const LocalFog_default_ = LocalFog;
;

/* harmony default export */ const EnvironmentalEffects_LocalFog = (Namespace/* default */.A .add ("LocalFog", LocalFog_default_));
;// ./src/x_ite/Components/EnvironmentalEffects/TextureBackground.js










function TextureBackground (executionContext)
{
   EnvironmentalEffects_X3DBackgroundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TextureBackground);
}

Object .assign (Object .setPrototypeOf (TextureBackground .prototype, EnvironmentalEffects_X3DBackgroundNode .prototype),
{
   initialize ()
   {
      EnvironmentalEffects_X3DBackgroundNode .prototype .initialize .call (this);

      this ._frontTexture  .addInterest ("set_texture__", this, 0);
      this ._backTexture   .addInterest ("set_texture__", this, 1);
      this ._leftTexture   .addInterest ("set_texture__", this, 2);
      this ._rightTexture  .addInterest ("set_texture__", this, 3);
      this ._topTexture    .addInterest ("set_texture__", this, 4);
      this ._bottomTexture .addInterest ("set_texture__", this, 5);

      this .set_texture__ (0, this ._frontTexture);
      this .set_texture__ (1, this ._backTexture);
      this .set_texture__ (2, this ._leftTexture);
      this .set_texture__ (3, this ._rightTexture);
      this .set_texture__ (4, this ._topTexture);
      this .set_texture__ (5, this ._bottomTexture);
   },
   set_texture__ (index, textureNode)
   {
      EnvironmentalEffects_X3DBackgroundNode .prototype .set_texture__ .call (this, index, Base_X3DCast (Base_X3DConstants .X3DTextureNode, textureNode));
   },
   traverse (type, renderObject)
   {
      EnvironmentalEffects_X3DBackgroundNode .prototype .traverse .call (this, type, renderObject);

      if (type !== Rendering_TraverseType .DISPLAY)
         return;

      for (const textureNode of this .getTextureNodes ())
         textureNode ?.traverse (type, renderObject);
   },
});

Object .defineProperties (TextureBackground,
{
   ... Core_X3DNode .getStaticProperties ("TextureBackground", "EnvironmentalEffects", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_bind",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "skyAngle",      new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "skyColor",      new x_ite_Fields .MFColor (new Numbers_Color3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "groundAngle",   new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "groundColor",   new x_ite_Fields .MFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isBound",       new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bindTime",      new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "frontTexture",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backTexture",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "leftTexture",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "rightTexture",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "topTexture",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "bottomTexture", new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const TextureBackground_default_ = TextureBackground;
;

/* harmony default export */ const EnvironmentalEffects_TextureBackground = (Namespace/* default */.A .add ("TextureBackground", TextureBackground_default_));
;// ./src/x_ite/Components/EnvironmentalEffectsComponent.js








const EnvironmentalEffectsComponent_default_ = {
   name: "EnvironmentalEffects",
   concreteNodes:
   [
      EnvironmentalEffects_Background,
      EnvironmentalEffects_Fog,
      EnvironmentalEffects_FogCoordinate,
      EnvironmentalEffects_LocalFog,
      EnvironmentalEffects_TextureBackground,
   ],
   abstractNodes:
   [
      EnvironmentalEffects_X3DBackgroundNode,
      EnvironmentalEffects_X3DFogObject,
   ],
};
;

/* harmony default export */ const EnvironmentalEffectsComponent = (Namespace/* default */.A .add ("EnvironmentalEffectsComponent", EnvironmentalEffectsComponent_default_));
;// ./src/x_ite/Components/EnvironmentalSensor/X3DEnvironmentalSensorNode.js






function X3DEnvironmentalSensorNode (executionContext)
{
   Core_X3DSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DEnvironmentalSensorNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "traversed", new x_ite_Fields .SFBool (true));

   // Units

   this ._size   .setUnit ("length");
   this ._center .setUnit ("length");

   // Private properties

   this .zeroTest         = false;
   this .currentTraversed = true;
}

Object .assign (Object .setPrototypeOf (X3DEnvironmentalSensorNode .prototype, Core_X3DSensorNode .prototype),
{
   initialize ()
   {
      Core_X3DSensorNode .prototype .initialize .call (this);

      this .getLive () .addInterest ("set_live__", this);

      this ._enabled   .addInterest ("set_live__", this);
      this ._size      .addInterest ("set_live__", this);
      this ._traversed .addInterest ("set_live__", this);

      this .set_live__ ();
   },
   set_live__ ()
   {
      if (this ._traversed .getValue () && this .getLive () .getValue () && this ._enabled .getValue () && !(this .zeroTest && this ._size. getValue () .equals (Numbers_Vector3 .ZERO)))
      {
         this .getBrowser () .sensorEvents () .addInterest ("update", this);
      }
      else
      {
         this .getBrowser () .sensorEvents () .removeInterest ("update", this);

         if (this ._isActive .getValue ())
         {
            this ._isActive = false;
            this ._exitTime = this .getBrowser () .getCurrentTime ();
         }
      }
   },
   setZeroTest (value)
   {
      this .zeroTest = value;
   },
   getZeroTest ()
   {
      return this .zeroTest;
   },
   setTraversed (value)
   {
      if (value)
      {
         if (this ._traversed .getValue () === false)
            this ._traversed = true;
      }
      else
      {
         if (this .currentTraversed !== this ._traversed .getValue ())
            this ._traversed = this .currentTraversed;
      }

      this .currentTraversed = value;
   },
   getTraversed ()
   {
      return this .currentTraversed;
   },
   update ()
   { },
});

Object .defineProperties (X3DEnvironmentalSensorNode, Core_X3DNode .getStaticProperties ("X3DEnvironmentalSensorNode", "EnvironmentalSensor", 1));

const X3DEnvironmentalSensorNode_default_ = X3DEnvironmentalSensorNode;
;

/* harmony default export */ const EnvironmentalSensor_X3DEnvironmentalSensorNode = (Namespace/* default */.A .add ("X3DEnvironmentalSensorNode", X3DEnvironmentalSensorNode_default_));
;// ./src/x_ite/Components/EnvironmentalSensor/ProximitySensor.js











function ProximitySensor (executionContext)
{
   EnvironmentalSensor_X3DEnvironmentalSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ProximitySensor);

   this .setCameraObject (true);
   this .setZeroTest (true);

   // Units

   this ._centerOfRotation_changed .setUnit ("length");
   this ._position_changed         .setUnit ("length");

   // Private properties

   this .min           = new Numbers_Vector3 ();
   this .max           = new Numbers_Vector3 ();
   this .layerNode     = null;
   this .modelMatrix   = new Numbers_Matrix4 ();
   this .inside        = false;
}

Object .assign (Object .setPrototypeOf (ProximitySensor .prototype, EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype),
{
   initialize ()
   {
      EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype .initialize .call (this);

      this ._size   .addInterest ("set_extents__", this);
      this ._center .addInterest ("set_extents__", this);

      this ._enabled   .addFieldInterest (this ._isVisibleObject);
      this ._traversed .addFieldInterest (this ._isCameraObject);

      this .setVisibleObject (this ._enabled .getValue ());

      this .set_extents__ ();
   },
   set_extents__ ()
   {
      const
         s  = this ._size .getValue (),
         c  = this ._center .getValue (),
         sx = s .x / 2,
         sy = s .y / 2,
         sz = s .z / 2,
         cx = c .x,
         cy = c .y,
         cz = c .z;

      this .min .set (cx - sx, cy - sy, cz - sz);
      this .max .set (cx + sx, cy + sy, cz + sz);
   },
   update: (() =>
   {
      const
         position               = new Numbers_Vector3 (),
         orientation            = new Numbers_Rotation4 (),
         centerOfRotation       = new Numbers_Vector3 (),
         centerOfRotationMatrix = new Numbers_Matrix4 ();

      return function ()
      {
         if (this .inside && this .getTraversed ())
         {
            if (this .layerNode)
            {
               const
                  browser        = this .getBrowser (),
                  viewpointNode  = this .layerNode .getViewpoint (),
                  invModelMatrix = this .modelMatrix .inverse ()

               centerOfRotationMatrix
                  .assign (viewpointNode .getModelMatrix ())
                  .translate (viewpointNode .getUserCenterOfRotation ())
                  .multRight (invModelMatrix)
                  .getTransform (centerOfRotation);

               invModelMatrix .multLeft (viewpointNode .getCameraSpaceMatrix ());

               if (this .layerNode === browser .getActiveLayer ())
               {
                  const pose = browser .getPose ();

                  if (pose)
                     invModelMatrix .multLeft (pose .cameraSpaceMatrix);
               }

               invModelMatrix .getTransform (position, orientation);

               if (this ._isActive .getValue ())
               {
                  if (!this ._position_changed .getValue () .equals (position))
                     this ._position_changed = position;

                  if (!this ._orientation_changed .getValue () .equals (orientation))
                     this ._orientation_changed = orientation;

                  if (!this ._centerOfRotation_changed .getValue () .equals (centerOfRotation))
                     this ._centerOfRotation_changed = centerOfRotation;
               }
               else
               {
                  this ._isActive                 = true;
                  this ._enterTime                = browser .getCurrentTime ();
                  this ._position_changed         = position;
                  this ._orientation_changed      = orientation;
                  this ._centerOfRotation_changed = centerOfRotation;
               }
            }
         }
         else
         {
            if (this ._isActive .getValue ())
            {
               this ._isActive = false;
               this ._exitTime = this .getBrowser () .getCurrentTime ();
            }
         }

         this .inside    = false;
         this .layerNode = null;

         this .setTraversed (false);
      };
   })(),
   traverse: (() =>
   {
      const invModelViewMatrix = new Numbers_Matrix4 ();

      return function (type, renderObject)
      {
         switch (type)
         {
            case Rendering_TraverseType .CAMERA:
            {
               this .layerNode = renderObject .getLayer ();
               this .modelMatrix .assign (renderObject .getModelViewMatrix () .get ());
               return;
            }
            case Rendering_TraverseType .DISPLAY:
            {
               this .setTraversed (true);

               if (this .inside)
                  return;

               if (this ._size .getValue () .equals (Numbers_Vector3 .NEGATIVE_ONE))
               {
                  this .inside = true;
               }
               else
               {
                  invModelViewMatrix .assign (renderObject .getModelViewMatrix () .get ()) .inverse ();

                  this .inside = this .containsPoint (invModelViewMatrix .origin);
               }

               return;
            }
         }
      };
   })(),
   containsPoint ({ x: px, y: py, z: pz })
   {
      const
         min = this .min,
         max = this .max;

      return min .x <= px &&
             max .x >= px &&
             min .y <= py &&
             max .y >= py &&
             min .z <= pz &&
             max .z >= pz;
   },
});

Object .defineProperties (ProximitySensor,
{
   ... Core_X3DNode .getStaticProperties ("ProximitySensor", "EnvironmentalSensor", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",                 new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",              new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",                  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "size",                     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "center",                   new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "enterTime",                new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "exitTime",                 new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",                 new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "position_changed",         new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "orientation_changed",      new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "centerOfRotation_changed", new x_ite_Fields .SFVec3f ()),
      ]),
      enumerable: true,
   },
});

const ProximitySensor_default_ = ProximitySensor;
;

/* harmony default export */ const EnvironmentalSensor_ProximitySensor = (Namespace/* default */.A .add ("ProximitySensor", ProximitySensor_default_));
;// ./src/x_ite/Components/EnvironmentalSensor/TransformSensor.js












const
   ModelMatrixCache  = Utility_ObjectCache (Numbers_Matrix4),
   TargetMatrixCache = Utility_ObjectCache (Numbers_Matrix4);

function TransformSensor (executionContext)
{
   EnvironmentalSensor_X3DEnvironmentalSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TransformSensor);

   this .setZeroTest (true);

   // Units

   this ._position_changed .setUnit ("length");

   // Private properties

   this .min            = new Numbers_Vector3 ();
   this .max            = new Numbers_Vector3 ();
   this .modelMatrices  = [ ];
   this .targetMatrices = [ ];
}

Object .assign (Object .setPrototypeOf (TransformSensor .prototype, EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype),
{
   initialize ()
   {
      EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype .initialize .call (this);

      this .getLive () .addInterest ("set_enabled__", this);

      this ._enabled      .addInterest ("set_enabled__",      this);
      this ._size         .addInterest ("set_enabled__",      this);
      this ._size         .addInterest ("set_extents__",      this);
      this ._center       .addInterest ("set_extents__",      this);
      this ._targetObject .addInterest ("set_targetObject__", this);

      this .set_extents__ ();
      this .set_targetObject__ ();
   },
   set_live__ ()
   { },
   set_enabled__ ()
   {
      if (this .getLive () .getValue () && this .targetObjectNode && this ._enabled .getValue () && !this ._size. getValue () .equals (Numbers_Vector3 .ZERO))
      {
         this .setPickableObject (true);
         this .getBrowser () .addTransformSensor (this);
         this .targetObjectNode .addTransformSensor (this);
      }
      else
      {
         this .setPickableObject (false);
         this .getBrowser () .removeTransformSensor (this);
         this .targetObjectNode ?.removeTransformSensor (this);

         if (this ._isActive .getValue ())
         {
            this ._isActive = false;
            this ._exitTime = this .getBrowser () .getCurrentTime ();
         }
      }
   },
   set_extents__ ()
   {
      const
         s  = this ._size .getValue (),
         c  = this ._center .getValue (),
         sx = s .x / 2,
         sy = s .y / 2,
         sz = s .z / 2,
         cx = c .x,
         cy = c .y,
         cz = c .z;

      this .min .set (cx - sx, cy - sy, cz - sz);
      this .max .set (cx + sx, cy + sy, cz + sz);
   },
   set_targetObject__ ()
   {
      this .targetObjectNode = Base_X3DCast (Base_X3DConstants .X3DGroupingNode, this ._targetObject)
         ?? Base_X3DCast (Base_X3DConstants .X3DShapeNode, this ._targetObject);

      this .set_enabled__ ();
   },
   traverse (type, renderObject)
   {
      this .modelMatrices .push (ModelMatrixCache .pop () .assign (renderObject .getModelViewMatrix () .get ()));
   },
   collect (targetMatrix)
   {
      this .targetMatrices .push (TargetMatrixCache .pop () .assign (targetMatrix));
   },
   process: (() =>
   {
      const
         position    = new Numbers_Vector3 (),
         orientation = new Numbers_Rotation4 ();

      return function ()
      {
         const
            modelMatrices  = this .modelMatrices,
            targetMatrices = this .targetMatrices,
            matrix         = this .intersects ();

         if (matrix)
         {
            matrix .getTransform (position, orientation);

            if (this ._isActive .getValue ())
            {
               if (!this ._position_changed .getValue () .equals (position))
                  this ._position_changed = position;

               if (!this ._orientation_changed .getValue () .equals (orientation))
                  this ._orientation_changed = orientation;
            }
            else
            {
               this ._isActive            = true;
               this ._enterTime           = this .getBrowser () .getCurrentTime ();
               this ._position_changed    = position;
               this ._orientation_changed = orientation;
            }
         }
         else
         {
            if (this ._isActive .getValue ())
            {
               this ._isActive = false;
               this ._exitTime = this .getBrowser () .getCurrentTime ();
            }
         }

         for (const modelMatrix of modelMatrices)
            ModelMatrixCache .push (modelMatrix);

         for (const targetMatrix of targetMatrices)
            TargetMatrixCache .push (targetMatrix);

         modelMatrices  .length = 0;
         targetMatrices .length = 0;
      };
   })(),
   intersects ()
   {
      const
         modelMatrices  = this .modelMatrices,
         targetMatrices = this .targetMatrices,
         always         = this ._size .getValue () .equals (Numbers_Vector3 .NEGATIVE_ONE);

      for (const modelMatrix of modelMatrices)
      {
         const invModelMatrix = modelMatrix .inverse ();

         for (const targetMatrix of targetMatrices)
         {
            const matrix = targetMatrix .multRight (invModelMatrix);

            if (always || this .containsPoint (matrix .origin))
            {
               return matrix;
            }
         }
      }

      return null;
   },
   containsPoint (point)
   {
      const
         min = this .min,
         max = this .max;

      return min .x <= point .x &&
             max .x >= point .x &&
             min .y <= point .y &&
             max .y >= point .y &&
             min .z <= point .z &&
             max .z >= point .z;
   },
});

Object .defineProperties (TransformSensor,
{
   ... Core_X3DNode .getStaticProperties ("TransformSensor", "EnvironmentalSensor", 3, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",         new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "size",                new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "center",              new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "enterTime",           new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "exitTime",            new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "position_changed",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "orientation_changed", new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "targetObject",        new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const TransformSensor_default_ = TransformSensor;
;

/* harmony default export */ const EnvironmentalSensor_TransformSensor = (Namespace/* default */.A .add ("TransformSensor", TransformSensor_default_));
;// ./src/x_ite/Components/EnvironmentalSensor/VisibilitySensor.js









function VisibilitySensor (executionContext)
{
   EnvironmentalSensor_X3DEnvironmentalSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .VisibilitySensor);

   this .setZeroTest (false);

   this .visible = false;
}

Object .assign (Object .setPrototypeOf (VisibilitySensor .prototype, EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype),
{
   initialize ()
   {
      EnvironmentalSensor_X3DEnvironmentalSensorNode .prototype .initialize .call (this);

      this ._enabled .addFieldInterest (this ._isVisibleObject);

      this .setVisibleObject (this ._enabled .getValue ());
   },
   update ()
   {
      if (this .visible && this .getTraversed ())
      {
         if (!this ._isActive .getValue ())
         {
            this ._isActive  = true;
            this ._enterTime = this .getBrowser () .getCurrentTime ();
         }

         this .visible = false;
      }
      else
      {
         if (this ._isActive .getValue ())
         {
            this ._isActive = false;
            this ._exitTime = this .getBrowser () .getCurrentTime ();
         }
      }

      this .setTraversed (false);
   },
   traverse: (() =>
   {
      const bbox = new Geometry_Box3 ();

      return function (type, renderObject)
      {
         this .setTraversed (true);

         if (this .visible)
            return;

         if (this ._size .getValue () .equals (Numbers_Vector3 .NEGATIVE_ONE))
         {
            this .visible = true;
         }
         else
         {
            bbox
               .set (this ._size .getValue (), this ._center .getValue ())
               .multRight (renderObject .getModelViewMatrix () .get ());

            this .visible = renderObject .getViewVolumes () .at (-1) .intersectsBox (bbox);
         }
      };
   })(),
});

Object .defineProperties (VisibilitySensor,
{
   ... Core_X3DNode .getStaticProperties ("VisibilitySensor", "EnvironmentalSensor", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "size",        new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "center",      new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "enterTime",   new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "exitTime",    new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",    new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const VisibilitySensor_default_ = VisibilitySensor;
;

/* harmony default export */ const EnvironmentalSensor_VisibilitySensor = (Namespace/* default */.A .add ("VisibilitySensor", VisibilitySensor_default_));
;// ./src/x_ite/Components/EnvironmentalSensorComponent.js





const EnvironmentalSensorComponent_default_ = {
   name: "EnvironmentalSensor",
   concreteNodes:
   [
      EnvironmentalSensor_ProximitySensor,
      EnvironmentalSensor_TransformSensor,
      EnvironmentalSensor_VisibilitySensor,
   ],
   abstractNodes:
   [
      EnvironmentalSensor_X3DEnvironmentalSensorNode,
   ],
};
;

/* harmony default export */ const EnvironmentalSensorComponent = (Namespace/* default */.A .add ("EnvironmentalSensorComponent", EnvironmentalSensorComponent_default_));
;// ./src/x_ite/Components/Followers/ColorChaser.js









const
   initialValue       = new Numbers_Vector3 (),
   initialDestination = new Numbers_Vector3 (),
   deltaOut           = new Numbers_Vector3 (),
   ColorChaser_vector             = new Numbers_Vector3 ();

function ColorChaser (executionContext)
{
   Followers_X3DChaserNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ColorChaser);
}

Object .assign (Object .setPrototypeOf (ColorChaser .prototype, Followers_X3DChaserNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
   getValue ()
   {
      return this ._set_value .getValue () .getHSV (ColorChaser_vector);
   },
   getDestination ()
   {
      return this ._set_destination .getValue () .getHSV (ColorChaser_vector);
   },
   getInitialValue ()
   {
      return this ._initialValue .getValue () .getHSV (initialValue);
   },
   getInitialDestination ()
   {
      return this ._initialDestination .getValue () .getHSV (initialDestination);
   },
   setValue (value)
   {
      this ._value_changed .setHSV (value .x, value .y, value .z);
   },
   interpolate (source, destination, weight)
   {
      return Numbers_Color3 .lerp (source, destination, weight, ColorChaser_vector);
   },
   step (value1, value2, t)
   {
      deltaOut .assign (this .output) .add (value1) .subtract (value2);

      //step .x = Algorithm .interval (step .x, 0, 2 * Math .PI);

      Numbers_Color3 .lerp (this .output, deltaOut, t, this .output);
   },
});

Object .defineProperties (ColorChaser,
{
   ... Core_X3DNode .getStaticProperties ("ColorChaser", "Followers", 1, "children", "3.3"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFColor ()),
      ]),
      enumerable: true,
   },
});

const ColorChaser_default_ = ColorChaser;
;

/* harmony default export */ const Followers_ColorChaser = (Namespace/* default */.A .add ("ColorChaser", ColorChaser_default_));
;// ./src/x_ite/Components/Followers/X3DDamperNode.js





function X3DDamperNode (executionContext)
{
   Followers_X3DFollowerNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DDamperNode);
}

Object .assign (Object .setPrototypeOf (X3DDamperNode .prototype, Followers_X3DFollowerNode .prototype),
{
   initialize ()
   {
      Followers_X3DFollowerNode .prototype .initialize .call (this);

      this ._order           .addInterest ("set_order__", this);
      this ._set_value       .addInterest ("set_value__", this);
      this ._set_destination .addInterest ("set_destination__", this);

      const
         buffer             = this .getBuffer (),
         initialValue       = this .getInitialValue (),
         initialDestination = this .getInitialDestination ();

      buffer [0] = this .duplicate (initialDestination);

      for (let i = 1, length = this .getOrder () + 1; i < length; ++ i)
         buffer [i] = this .duplicate (initialValue);

      if (this .equals (initialDestination, initialValue, this .getTolerance ()))
         this .setValue (initialDestination);

      else
         this .set_active__ (true);
   },
   getOrder ()
   {
      return Math_Algorithm .clamp (this ._order .getValue (), 0, 5);
   },
   getTolerance ()
   {
      if (this ._tolerance .getValue () < 0)
         return 1e-4;

      return this ._tolerance .getValue ();
   },
   prepareEvents ()
   {
      const buffer = this .getBuffer ();

      let order  = buffer .length - 1;

      if (this ._tau .getValue ())
      {
         const
            delta = 1 / this .getBrowser () .currentFrameRate,
            alpha = Math .exp (-delta / this ._tau .getValue ());

         for (let i = 0; i < order; ++ i)
         {
            this .assign (buffer, i + 1, this .interpolate (buffer [i], buffer [i + 1], alpha));
         }

         this .setValue (buffer [order]);

         if (!this .equals (buffer [order], buffer [0], this .getTolerance ()))
            return;
      }
      else
      {
         this .setValue (buffer [0]);

         order = 0;
      }

      for (let i = 1, length = buffer .length; i < length; ++ i)
         this .assign (buffer, i, buffer [order]);

      this .set_active__ (false);
   },
   set_value__ ()
   {
      const
         buffer = this .getBuffer (),
         value  = this .getValue ();

      for (let i = 1, length = buffer .length; i < length; ++ i)
         this .assign (buffer, i, value);

      this .setValue (value);

      this .set_active__ (true);
   },
   set_destination__ ()
   {
      this .assign (this .getBuffer (), 0, this .getDestination ());

      this .set_active__ (true);
   },
   set_order__ ()
   {
      const
         buffer = this .getBuffer (),
         value  = buffer [buffer .length - 1];

      for (let i = buffer .length, length = this .getOrder () + 1; i < length; ++ i)
         buffer [i] = this .duplicate (value);

      buffer .length = length;
   },
});

Object .defineProperties (X3DDamperNode, Core_X3DNode .getStaticProperties ("X3DDamperNode", "Followers", 1));

const X3DDamperNode_default_ = X3DDamperNode;
;

/* harmony default export */ const Followers_X3DDamperNode = (Namespace/* default */.A .add ("X3DDamperNode", X3DDamperNode_default_));
;// ./src/x_ite/Components/Followers/ColorDamper.js









const
   ColorDamper_a                  = new Numbers_Vector3 (),
   ColorDamper_initialValue       = new Numbers_Vector3 (),
   ColorDamper_initialDestination = new Numbers_Vector3 (),
   ColorDamper_vector             = new Numbers_Vector3 ();

function ColorDamper (executionContext)
{
   Followers_X3DDamperNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ColorDamper);
}

Object .assign (Object .setPrototypeOf (ColorDamper .prototype, Followers_X3DDamperNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
   getValue ()
   {
      return this ._set_value .getValue () .getHSV (ColorDamper_vector);
   },
   getDestination ()
   {
      return this ._set_destination .getValue () .getHSV (ColorDamper_vector);
   },
   getInitialValue ()
   {
      return this ._initialValue .getValue () .getHSV (ColorDamper_initialValue);
   },
   getInitialDestination ()
   {
      return this ._initialDestination .getValue () .getHSV (ColorDamper_initialDestination);
   },
   setValue (value)
   {
      this ._value_changed .setHSV (value .x, value .y, value .z);
   },
   equals (lhs, rhs, tolerance)
   {
      return ColorDamper_a .assign (lhs) .subtract (rhs) .norm () < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return Numbers_Color3 .lerp (source, destination, weight, ColorDamper_vector);
   },
});

Object .defineProperties (ColorDamper,
{
   ... Core_X3DNode .getStaticProperties ("ColorDamper", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFColor ()),
      ]),
      enumerable: true,
   },
});

const ColorDamper_default_ = ColorDamper;
;

/* harmony default export */ const Followers_ColorDamper = (Namespace/* default */.A .add ("ColorDamper", ColorDamper_default_));
;// ./src/x_ite/Browser/Followers/X3DArrayFollowerTemplate.js
function X3DArrayFollowerTemplate (Type)
{
   function X3DArrayFollowerObject ()
   {
      this .array = this .getArray ();
      this .zero  = this .getVector ();
   }

   Object .assign (X3DArrayFollowerObject .prototype,
   {
      getArray ()
      {
         const array = [ ];

         array .assign = function (value)
         {
            const
               minLength = Math .min (this .length, value .length),
               maxLength = value .length;

            if (Array .isArray (value))
            {
               for (let i = 0; i < minLength; ++ i)
                  this [i] .assign (value [i]);

               for (let i = minLength; i < maxLength; ++ i)
                  this [i] = value [i] .copy ();

               this .length = maxLength;
            }
            else
            {
               for (let i = 0; i < minLength; ++ i)
                  this [i] .assign (value [i] .getValue ());

               for (let i = minLength; i < maxLength; ++ i)
                  this [i] = value [i] .getValue () .copy ();

               this .length = maxLength;
            }
         };

         return array;
      },
      getValue ()
      {
         return this ._set_value;
      },
      getDestination ()
      {
         return this ._set_destination;
      },
      getInitialValue ()
      {
         return this ._initialValue;
      },
      getInitialDestination ()
      {
         return this ._initialDestination;
      },
      setValue (value)
      {
         if (Array .isArray (value))
         {
            const
               value_changed = this ._value_changed,
               length        = value .length;

            for (let i = 0; i < length; ++ i)
               value_changed [i] = value [i];

            value_changed .length = length;
         }
         else
         {
            this ._value_changed = value;
         }
      },
      duplicate (value)
      {
         const array = this .getArray ();

         array .assign (value);

         return array;
      },
      equals (lhs, rhs, tolerance)
      {
         if (lhs .length !== rhs .length)
            return false;

         const a = this .a;

         let distance = 0;

         for (let i = 0, length = lhs .length; i < length; ++ i)
           distance = Math .max (a .assign (lhs [i]) .subtract (rhs [i]) .norm (), distance);

         return distance < tolerance;
      },
      interpolate (source, destination, weight)
      {
         const array = this .array;

         array .assign (source);

         for (let i = 0, length = array .length; i < length; ++ i)
            array [i] .lerp (destination [i] || this .zero, weight);

         return array;
      },
      set_destination__ ()
      {
         const
            buffers = this .getBuffer (),
            l       = this ._set_destination .length;

         for (let i = 0, length = buffers .length; i < length; ++ i)
         {
            const buffer = buffers [i];

            for (let b = buffer .length; b < l; ++ b)
               buffer [b] = this .getVector ();

            buffer .length = l;
         }

         Type .prototype .set_destination__ .call (this);
      },
   });

   return X3DArrayFollowerObject;
}

const X3DArrayFollowerTemplate_default_ = X3DArrayFollowerTemplate;
;

/* harmony default export */ const Followers_X3DArrayFollowerTemplate = (Namespace/* default */.A .add ("X3DArrayFollowerTemplate", X3DArrayFollowerTemplate_default_));
;// ./src/x_ite/Browser/Followers/X3DArrayChaserTemplate.js


function X3DArrayChaserTemplate (Type)
{
   const X3DArrayFollower = Followers_X3DArrayFollowerTemplate (Type);

   function X3DArrayChaserObject ()
   {
      X3DArrayFollower .call (this);

      this .array = this .getArray ();
   }

   Object .assign (X3DArrayChaserObject .prototype,
      X3DArrayFollower .prototype,
   {
      step (value1, value2, t)
      {
         const
            output   = this .output,
            deltaOut = this .deltaOut;

         for (let i = 0, length = output .length; i < length; ++ i)
            output [i] .add (deltaOut .assign (value1 [i] || this .zero) .subtract (value2 [i] || this .zero) .multiply (t));
      },
   });

   return X3DArrayChaserObject;
}

const X3DArrayChaserTemplate_default_ = X3DArrayChaserTemplate;
;

/* harmony default export */ const Followers_X3DArrayChaserTemplate = (Namespace/* default */.A .add ("X3DArrayChaserTemplate", X3DArrayChaserTemplate_default_));
;// ./src/x_ite/Components/Followers/CoordinateChaser.js









const X3DArrayChaserObject = Followers_X3DArrayChaserTemplate (Followers_X3DChaserNode);

function CoordinateChaser (executionContext)
{
   Followers_X3DChaserNode        .call (this, executionContext);
   X3DArrayChaserObject .call (this, executionContext);

   this .addType (Base_X3DConstants .CoordinateChaser);
}

Object .assign (Object .setPrototypeOf (CoordinateChaser .prototype, Followers_X3DChaserNode .prototype),
   X3DArrayChaserObject .prototype,
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
});

Object .defineProperties (CoordinateChaser,
{
   ... Core_X3DNode .getStaticProperties ("CoordinateChaser", "Followers", 1, "children", "3.3"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .MFVec3f (new Numbers_Vector3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .MFVec3f (new Numbers_Vector3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const CoordinateChaser_default_ = CoordinateChaser;
;

/* harmony default export */ const Followers_CoordinateChaser = (Namespace/* default */.A .add ("CoordinateChaser", CoordinateChaser_default_));
;// ./src/x_ite/Components/Followers/CoordinateDamper.js









const X3DArrayFollowerObject = Followers_X3DArrayFollowerTemplate (Followers_X3DDamperNode);

function CoordinateDamper (executionContext)
{
   Followers_X3DDamperNode          .call (this, executionContext);
   X3DArrayFollowerObject .call (this, executionContext);

   this .addType (Base_X3DConstants .CoordinateDamper);
}

Object .assign (Object .setPrototypeOf (CoordinateDamper .prototype, Followers_X3DDamperNode .prototype),
   X3DArrayFollowerObject .prototype,
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
});

Object .defineProperties (CoordinateDamper,
{
   ... Core_X3DNode .getStaticProperties ("CoordinateDamper", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .MFVec3f (new Numbers_Vector3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .MFVec3f (new Numbers_Vector3 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const CoordinateDamper_default_ = CoordinateDamper;
;

/* harmony default export */ const Followers_CoordinateDamper = (Namespace/* default */.A .add ("CoordinateDamper", CoordinateDamper_default_));
;// ./src/x_ite/Components/Followers/OrientationDamper.js








const
   OrientationDamper_a        = new Numbers_Rotation4 (),
   OrientationDamper_rotation = new Numbers_Rotation4 ();

function OrientationDamper (executionContext)
{
   Followers_X3DDamperNode .call (this, executionContext);

   this .addType (Base_X3DConstants .OrientationDamper);

   // Units

   this ._set_value          .setUnit ("angle");
   this ._set_destination    .setUnit ("angle");
   this ._initialValue       .setUnit ("angle");
   this ._initialDestination .setUnit ("angle");
   this ._value_changed      .setUnit ("angle");
}

Object .assign (Object .setPrototypeOf (OrientationDamper .prototype, Followers_X3DDamperNode .prototype),
{
   getVector ()
   {
      return new Numbers_Rotation4 ();
   },
   equals (lhs, rhs, tolerance)
   {
      OrientationDamper_a .assign (lhs) .inverse () .multRight (rhs);

      return Math .abs (OrientationDamper_a .angle) < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return OrientationDamper_rotation .assign (source) .slerp (destination, weight);
   },
});

Object .defineProperties (OrientationDamper,
{
   ... Core_X3DNode .getStaticProperties ("OrientationDamper", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFRotation (0, 1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFRotation (0, 1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFRotation ()),
      ]),
      enumerable: true,
   },
});

const OrientationDamper_default_ = OrientationDamper;
;

/* harmony default export */ const Followers_OrientationDamper = (Namespace/* default */.A .add ("OrientationDamper", OrientationDamper_default_));
;// ./src/x_ite/Components/Followers/PositionChaser2D.js








function PositionChaser2D (executionContext)
{
   Followers_X3DChaserNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionChaser2D);
}

Object .assign (Object .setPrototypeOf (PositionChaser2D .prototype, Followers_X3DChaserNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector2 ();
   },
});

Object .defineProperties (PositionChaser2D,
{
   ... Core_X3DNode .getStaticProperties ("PositionChaser2D", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFVec2f ()),
      ]),
      enumerable: true,
   },
});

const PositionChaser2D_default_ = PositionChaser2D;
;

/* harmony default export */ const Followers_PositionChaser2D = (Namespace/* default */.A .add ("PositionChaser2D", PositionChaser2D_default_));
;// ./src/x_ite/Components/Followers/PositionDamper.js








function PositionDamper (executionContext)
{
   Followers_X3DDamperNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionDamper);
}

Object .assign (Object .setPrototypeOf (PositionDamper .prototype, Followers_X3DDamperNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector3 ();
   },
});

Object .defineProperties (PositionDamper,
{
   ... Core_X3DNode .getStaticProperties ("PositionDamper", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const PositionDamper_default_ = PositionDamper;
;

/* harmony default export */ const Followers_PositionDamper = (Namespace/* default */.A .add ("PositionDamper", PositionDamper_default_));
;// ./src/x_ite/Components/Followers/PositionDamper2D.js








function PositionDamper2D (executionContext)
{
   Followers_X3DDamperNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionDamper2D);
}

Object .assign (Object .setPrototypeOf (PositionDamper2D .prototype, Followers_X3DDamperNode .prototype),
{
   getVector ()
   {
      return new Numbers_Vector2 ();
   },
});

Object .defineProperties (PositionDamper2D,
{
   ... Core_X3DNode .getStaticProperties ("PositionDamper2D", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFVec2f ()),
      ]),
      enumerable: true,
   },
});

const PositionDamper2D_default_ = PositionDamper2D;
;

/* harmony default export */ const Followers_PositionDamper2D = (Namespace/* default */.A .add ("PositionDamper2D", PositionDamper2D_default_));
;// ./src/x_ite/Components/Followers/ScalarChaser.js








function ScalarChaser (executionContext)
{
   Followers_X3DChaserNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ScalarChaser);
}

Object .assign (Object .setPrototypeOf (ScalarChaser .prototype, Followers_X3DChaserNode .prototype),
{
   getVector ()
   {
      return 0;
   },
   setPreviousValue (value)
   {
      this .previousValue = value;
   },
   setDestination (value)
   {
      this .destination = value;
   },
   duplicate (value)
   {
      return value;
   },
   assign (buffer, i, value)
   {
      buffer [i] = value;
   },
   equals (lhs, rhs, tolerance)
   {
      return Math .abs (lhs - rhs) < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return Math_Algorithm .lerp (source, destination, weight);
   },
   step (value1, value2, t)
   {
      this .output += (value1 - value2) * t;
   },
});

Object .defineProperties (ScalarChaser,
{
   ... Core_X3DNode .getStaticProperties ("ScalarChaser", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const ScalarChaser_default_ = ScalarChaser;
;

/* harmony default export */ const Followers_ScalarChaser = (Namespace/* default */.A .add ("ScalarChaser", ScalarChaser_default_));
;// ./src/x_ite/Components/Followers/ScalarDamper.js








function ScalarDamper (executionContext)
{
   Followers_X3DDamperNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ScalarDamper);
}

Object .assign (Object .setPrototypeOf (ScalarDamper .prototype, Followers_X3DDamperNode .prototype),
{
   getVector ()
   {
      return 0;
   },
   duplicate (value)
   {
      return value;
   },
   assign (buffer, i, value)
   {
      buffer [i] = value;
   },
   equals (lhs, rhs, tolerance)
   {
      return Math .abs (lhs - rhs) < tolerance;
   },
   interpolate (source, destination, weight)
   {
      return Math_Algorithm .lerp (source, destination, weight);
   },
});

Object .defineProperties (ScalarDamper,
{
   ... Core_X3DNode .getStaticProperties ("ScalarDamper", "Followers", 1, "children", "3.3"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const ScalarDamper_default_ = ScalarDamper;
;

/* harmony default export */ const Followers_ScalarDamper = (Namespace/* default */.A .add ("ScalarDamper", ScalarDamper_default_));
;// ./src/x_ite/Components/Followers/TexCoordChaser2D.js









const TexCoordChaser2D_X3DArrayChaserObject = Followers_X3DArrayChaserTemplate (Followers_X3DChaserNode);

function TexCoordChaser2D (executionContext)
{
   Followers_X3DChaserNode        .call (this, executionContext);
   TexCoordChaser2D_X3DArrayChaserObject .call (this, executionContext);

   this .addType (Base_X3DConstants .TexCoordChaser2D);
}

Object .assign (Object .setPrototypeOf (TexCoordChaser2D .prototype, Followers_X3DChaserNode .prototype),
   TexCoordChaser2D_X3DArrayChaserObject .prototype,
{
   getVector ()
   {
      return new Numbers_Vector2 ();
   },
});

Object .defineProperties (TexCoordChaser2D,
{
   ... Core_X3DNode .getStaticProperties ("TexCoordChaser2D", "Followers", 1, "children", "3.3"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "duration",           new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .MFVec2f ()),
      ]),
      enumerable: true,
   },
});

const TexCoordChaser2D_default_ = TexCoordChaser2D;
;

/* harmony default export */ const Followers_TexCoordChaser2D = (Namespace/* default */.A .add ("TexCoordChaser2D", TexCoordChaser2D_default_));
;// ./src/x_ite/Components/Followers/TexCoordDamper2D.js









const TexCoordDamper2D_X3DArrayFollowerObject = Followers_X3DArrayFollowerTemplate (Followers_X3DDamperNode);

function TexCoordDamper2D (executionContext)
{
   Followers_X3DDamperNode          .call (this, executionContext);
   TexCoordDamper2D_X3DArrayFollowerObject .call (this, executionContext);

   this .addType (Base_X3DConstants .TexCoordDamper2D);
}

Object .assign (Object .setPrototypeOf (TexCoordDamper2D .prototype, Followers_X3DDamperNode .prototype),
   TexCoordDamper2D_X3DArrayFollowerObject .prototype,
{
   getVector ()
   {
      return new Numbers_Vector2 ();
   },
});

Object .defineProperties (TexCoordDamper2D,
{
   ... Core_X3DNode .getStaticProperties ("TexCoordDamper2D", "Followers", 1, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_value",          new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_destination",    new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialValue",       new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "initialDestination", new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "order",              new x_ite_Fields .SFInt32 (3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tau",                new x_ite_Fields .SFTime (0.3)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tolerance",          new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "value_changed",      new x_ite_Fields .MFVec2f ()),
      ]),
      enumerable: true,
   },
});

const TexCoordDamper2D_default_ = TexCoordDamper2D;
;

/* harmony default export */ const Followers_TexCoordDamper2D = (Namespace/* default */.A .add ("TexCoordDamper2D", TexCoordDamper2D_default_));
;// ./src/x_ite/Components/FollowersComponent.js


















const FollowersComponent_default_ = {
   name: "Followers",
   concreteNodes:
   [
      Followers_ColorChaser,
      Followers_ColorDamper,
      Followers_CoordinateChaser,
      Followers_CoordinateDamper,
      Followers_OrientationChaser,
      Followers_OrientationDamper,
      Followers_PositionChaser,
      Followers_PositionChaser2D,
      Followers_PositionDamper,
      Followers_PositionDamper2D,
      Followers_ScalarChaser,
      Followers_ScalarDamper,
      Followers_TexCoordChaser2D,
      Followers_TexCoordDamper2D,
   ],
   abstractNodes:
   [
      Followers_X3DChaserNode,
      Followers_X3DDamperNode,
      Followers_X3DFollowerNode,
   ],
};
;

/* harmony default export */ const FollowersComponent = (Namespace/* default */.A .add ("FollowersComponent", FollowersComponent_default_));
;// ./src/x_ite/Components/Geometry3D/Box.js








function Box (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Box);

   // Units

   this ._size .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (Box .prototype, Rendering_X3DGeometryNode .prototype),
{
   build: (() =>
   {
      const defaultSize = new Numbers_Vector3 (2);

      return function ()
      {
         const
            options     = this .getBrowser () .getBoxOptions (),
            geometry    = options .getGeometry (),
            size        = this ._size .getValue (),
            vertexArray = this .getVertices ();

         this .getMultiTexCoords () .push (... geometry .getMultiTexCoords ());
         this .getTangents () .assign (geometry .getTangents ());
         this .getNormals ()  .assign (geometry .getNormals ());

         if (size .equals (defaultSize))
         {
            vertexArray .assign (geometry .getVertices ());

            this .getMin () .assign (geometry .getMin ());
            this .getMax () .assign (geometry .getMax ());
         }
         else
         {
            const
               x               = Math .abs (size .x / 2),
               y               = Math .abs (size .y / 2),
               z               = Math .abs (size .z / 2),
               defaultVertices = geometry .getVertices ();

            for (let i = 0, length = defaultVertices .length; i < length; i += 4)
            {
               vertexArray .push (x * defaultVertices [i],
                                  y * defaultVertices [i + 1],
                                  z * defaultVertices [i + 2],
                                  1);
            }

            this .getMin () .set (-x, -y, -z);
            this .getMax () .set ( x,  y,  z);
         }

         this .setSolid (this ._solid .getValue ());
      };
   })(),
});

Object .defineProperties (Box,
{
   ... Core_X3DNode .getStaticProperties ("Box", "Geometry3D", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "size",     new x_ite_Fields .SFVec3f (2, 2, 2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",    new x_ite_Fields .SFBool (true)),
      ]),
      enumerable: true,
   },
});

const Box_default_ = Box;
;

/* harmony default export */ const Geometry3D_Box = (Namespace/* default */.A .add ("Box", Box_default_));
;// ./src/x_ite/Components/Geometry3D/Cone.js









function Cone (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Cone);

   // Units

   this ._height       .setUnit ("length");
   this ._bottomRadius .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (Cone .prototype, Rendering_X3DGeometryNode .prototype),
{
   set_live__ ()
   {
      this .connectOptions (this .getBrowser () .getConeOptions ());
   },
   build ()
   {
      const
         options       = this .getBrowser () .getConeOptions (),
         height        = Math .abs (this ._height .getValue ()),
         height1_2     = height / 2,
         bottomRadius  = Math .abs (this ._bottomRadius .getValue ()),
         texCoordArray = this .getTexCoords (),
         normalArray   = this .getNormals (),
         vertexArray   = this .getVertices ();

      this .getMultiTexCoords () .push (texCoordArray);

      if (this ._side .getValue ())
      {
         const
            geometry        = options .getSideGeometry (),
            defaultNormals  = geometry .getNormals (),
            defaultVertices = geometry .getVertices (),
            v1              = new Numbers_Vector3 (),
            rz              = new Numbers_Rotation4 (1, 0, 0, -Math .atan (bottomRadius / height)),
            rx              = new Numbers_Rotation4 ();

         for (const t of geometry .getMultiTexCoords () [0])
            texCoordArray .push (t);

         for (let i = 0, length = defaultNormals .length; i < length; i += 3)
         {
            v1 .set (defaultNormals [i], 0, defaultNormals [i + 2]),
            rx .setVectors (Numbers_Vector3 .Z_AXIS, v1) .multLeft (rz) .multVecRot (v1 .set (0, 0, 1));

            normalArray .push (... v1);
         }

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (bottomRadius * defaultVertices [i],
                               height1_2    * defaultVertices [i + 1],
                               bottomRadius * defaultVertices [i + 2],
                               1);
         }
      }

      if (this ._bottom .getValue ())
      {
         const
            geometry        = options .getBottomGeometry (),
            defaultVertices = geometry .getVertices ();

         for (const t of geometry .getMultiTexCoords () [0])
            texCoordArray .push (t);

         for (const n of geometry .getNormals ())
            normalArray .push (n);

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (bottomRadius * defaultVertices [i],
                               height1_2    * defaultVertices [i + 1],
                               bottomRadius * defaultVertices [i + 2],
                               1);
         }
      }

      this .setSolid (this ._solid .getValue ());
      this .setExtents ();
   },
   setExtents ()
   {
      const
         bottomRadius = this ._bottomRadius .getValue (),
         y1           = this ._height .getValue () / 2,
         y2           = -y1;

      if (!this ._side .getValue () && !this ._bottom .getValue ())
      {
         this .getMin () .set (0);
         this .getMax () .set (0);
      }
      else if (!this ._side .getValue ())
      {
         this .getMin () .set (-bottomRadius, y2, -bottomRadius);
         this .getMax () .set ( bottomRadius, y2,  bottomRadius);
      }
      else
      {
         this .getMin () .set (-bottomRadius, y2, -bottomRadius);
         this .getMax () .set ( bottomRadius, y1, bottomRadius);
      }
   },
});

Object .defineProperties (Cone,
{
   ... Core_X3DNode .getStaticProperties ("Cone", "Geometry3D", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "side",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bottom",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "height",       new x_ite_Fields .SFFloat (2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bottomRadius", new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",        new x_ite_Fields .SFBool (true)),
      ]),
      enumerable: true,
   },
});

const Cone_default_ = Cone;
;

/* harmony default export */ const Geometry3D_Cone = (Namespace/* default */.A .add ("Cone", Cone_default_));
;// ./src/x_ite/Components/Geometry3D/Cylinder.js







function Cylinder (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Cylinder);

   // Units

   this ._height .setUnit ("length");
   this ._radius .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (Cylinder .prototype, Rendering_X3DGeometryNode .prototype),
{
   set_live__ ()
   {
      this .connectOptions (this .getBrowser () .getCylinderOptions ());
   },
   build ()
   {
      const
         options       = this .getBrowser () .getCylinderOptions (),
         height1_2     = Math .abs (this ._height .getValue ()) / 2,
         radius        = Math .abs (this ._radius .getValue ()),
         texCoordArray = this .getTexCoords (),
         tangentArray  = this .getTangents (),
         normalArray   = this .getNormals (),
         vertexArray   = this .getVertices ();

      this .getMultiTexCoords () .push (texCoordArray);

      if (this ._side .getValue ())
      {
         const
            geometry        = options .getSideGeometry (),
            defaultVertices = geometry .getVertices ();

         for (const t of geometry .getMultiTexCoords () [0])
            texCoordArray .push (t);

         for (const t of geometry .getTangents ())
            tangentArray .push (t);

         for (const n of geometry .getNormals ())
            normalArray .push (n);

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (radius    * defaultVertices [i],
                               height1_2 * defaultVertices [i + 1],
                               radius    * defaultVertices [i + 2],
                               1);
         }
      }

      if (this ._top .getValue ())
      {
         const
            geometry        = options .getTopGeometry (),
            defaultVertices = geometry .getVertices ();

         for (const t of geometry .getMultiTexCoords () [0])
            texCoordArray .push (t);

         for (const t of geometry .getTangents ())
            tangentArray .push (t);

         for (const n of geometry .getNormals ())
            normalArray .push (n);

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (radius    * defaultVertices [i],
                               height1_2 * defaultVertices [i + 1],
                               radius    * defaultVertices [i + 2],
                               1);
         }
      }

      if (this ._bottom .getValue ())
      {
         const
            geometry        = options .getBottomGeometry (),
            defaultVertices = geometry .getVertices ();

         for (const t of geometry .getMultiTexCoords () [0])
            texCoordArray .push (t);

         for (const t of geometry .getTangents ())
            tangentArray .push (t);

         for (const n of geometry .getNormals ())
            normalArray .push (n);

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (radius    * defaultVertices [i],
                               height1_2 * defaultVertices [i + 1],
                               radius    * defaultVertices [i + 2],
                               1);
         }
      }

      this .setSolid (this ._solid .getValue ());
      this .setExtents ();
   },
   setExtents ()
   {
      const
         radius = this ._radius .getValue (),
         y1     = this ._height .getValue () / 2,
         y2     = -y1;

      if (!this ._top .getValue () && !this ._side .getValue () && !this ._bottom .getValue ())
      {
         this .getMin () .set (0);
         this .getMax () .set (0);
      }

      else if (!this ._top .getValue () && !this ._side .getValue ())
      {
         this .getMin () .set (-radius, y2, -radius);
         this .getMax () .set ( radius, y2,  radius);
      }

      else if (!this ._bottom .getValue () && !this ._side .getValue ())
      {
         this .getMin () .set (-radius, y1, -radius);
         this .getMax () .set ( radius, y1,  radius);
      }

      else
      {
         this .getMin () .set (-radius, y2, -radius);
         this .getMax () .set ( radius, y1,  radius);
      }
   },
});

Object .defineProperties (Cylinder,
{
   ... Core_X3DNode .getStaticProperties ("Cylinder", "Geometry3D", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "top",      new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "side",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bottom",   new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "height",   new x_ite_Fields .SFFloat (2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "radius",   new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",    new x_ite_Fields .SFBool (true)),
      ]),
      enumerable: true,
   },
});

const Cylinder_default_ = Cylinder;
;

/* harmony default export */ const Geometry3D_Cylinder = (Namespace/* default */.A .add ("Cylinder", Cylinder_default_));
;// ./src/x_ite/Components/Geometry3D/ElevationGrid.js










function ElevationGrid (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ElevationGrid);

   // Units

   this ._set_height  .setUnit ("length");
   this ._xSpacing    .setUnit ("length");
   this ._zSpacing    .setUnit ("length");
   this ._creaseAngle .setUnit ("angle");
   this ._height      .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (ElevationGrid .prototype, Rendering_X3DGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometryNode .prototype .initialize .call (this);

      this ._set_height .addFieldInterest (this ._height);
      this ._attrib     .addInterest ("set_attrib__",   this);
      this ._fogCoord   .addInterest ("set_fogCoord__", this);
      this ._color      .addInterest ("set_color__",    this);
      this ._texCoord   .addInterest ("set_texCoord__", this);
      this ._tangent    .addInterest ("set_tangent__",  this);
      this ._normal     .addInterest ("set_normal__",   this);

      this .set_attrib__ ();
      this .set_fogCoord__ ();
      this .set_color__ ();
      this .set_texCoord__ ();
      this .set_tangent__ ();
      this .set_normal__ ();
   },
   getHeight (index)
   {
      if (index < this ._height .length)
         return this ._height [index];

      return 0;
   },
   createTexCoords ()
   {
      const
         texCoords  = [ ],
         xDimension = this ._xDimension .getValue (),
         zDimension = this ._zDimension .getValue (),
         xSize      = xDimension - 1,
         zSize      = zDimension - 1;

      for (let z = 0; z < zDimension; ++ z)
      {
         for (let x = 0; x < xDimension; ++ x)
            texCoords .push (new Numbers_Vector2 (x / xSize, z / zSize));
      }

      return texCoords;
   },
   createNormals (points, coordIndex, creaseAngle)
   {
      const
         cw          = ! this ._ccw .getValue (),
         normalIndex = new Map (),
         normals     = [ ];

      for (let p = 0, length = points .length; p < length; ++ p)
         normalIndex .set (p, [ ]);

      for (let c = 0, length = coordIndex .length; c < length; c += 3)
      {
         const
            c0 = coordIndex [c],
            c1 = coordIndex [c + 1],
            c2 = coordIndex [c + 2];

         normalIndex .get (c0) .push (normals .length);
         normalIndex .get (c1) .push (normals .length + 1);
         normalIndex .get (c2) .push (normals .length + 2);

         const normal = Geometry_Triangle3 .normal (points [c0], points [c1], points [c2], new Numbers_Vector3 ());

         if (cw)
            normal .negate ();

         normals .push (normal);
         normals .push (normal);
         normals .push (normal);
      }

      if (!this ._normalPerVertex .getValue ())
         return normals;

      return this .refineNormals (normalIndex, normals, creaseAngle ?? this ._creaseAngle .getValue ());
   },
   createCoordIndex ()
   {
      // p1 - p4
      //  | \ |
      // p2 - p3

      const
         coordIndex = [ ],
         xDimension = this ._xDimension .getValue (),
         zDimension = this ._zDimension .getValue (),
         xSize      = xDimension - 1,
         zSize      = zDimension - 1;

      for (let z = 0; z < zSize; ++ z)
      {
         for (let x = 0; x < xSize; ++ x)
         {
            const
               i1 =       z * xDimension + x,
               i2 = (z + 1) * xDimension + x,
               i3 = (z + 1) * xDimension + (x + 1),
               i4 =       z * xDimension + (x + 1);

            coordIndex .push (i1); // p1
            coordIndex .push (i2); // p2
            coordIndex .push (i3); // p3

            coordIndex .push (i1); // p1
            coordIndex .push (i3); // p3
            coordIndex .push (i4); // p4
         }
      }

      return coordIndex;
   },
   createPoints ()
   {
      const
         points     = [ ],
         xDimension = this ._xDimension .getValue (),
         zDimension = this ._zDimension .getValue (),
         xSpacing   = this ._xSpacing .getValue (),
         zSpacing   = this ._zSpacing .getValue ();

      for (let z = 0; z < zDimension; ++ z)
      {
         for (let x = 0; x < xDimension; ++ x)
         {
            points .push (new Numbers_Vector3 (xSpacing * x,
                                       this .getHeight (x + z * xDimension),
                                       zSpacing * z));
         }
      }

      return points;
   },
   build ()
   {
      if (this ._xDimension .getValue () < 2 || this ._zDimension .getValue () < 2)
         return;

      const
         colorPerVertex     = this ._colorPerVertex .getValue (),
         normalPerVertex    = this ._normalPerVertex .getValue (),
         coordIndex         = this .createCoordIndex (),
         coordIndicesArray  = this .getCoordIndices (),
         attribNodes        = this .getAttrib (),
         numAttribNodes     = attribNodes .length,
         attribArrays       = this .getAttribs (),
         fogCoordNode       = this .getFogCoord (),
         colorNode          = this .getColor (),
         texCoordNode       = this .getTexCoord (),
         tangentNode        = this .getTangent (),
         normalNode         = this .getNormal (),
         points             = this .createPoints (),
         fogDepthArray      = this .getFogDepths (),
         colorArray         = this .getColors (),
         multiTexCoordArray = this .getMultiTexCoords (),
         tangentArray       = this .getTangents (),
         normalArray        = this .getNormals (),
         vertexArray        = this .getVertices (),
         numCoordIndices    = coordIndex .length;

      let texCoords, texCoordArray;

      if (texCoordNode)
      {
         texCoordNode .init (multiTexCoordArray);
      }
      else
      {
         texCoords     = this .createTexCoords (),
         texCoordArray = this .getTexCoords ();

         multiTexCoordArray .push (texCoordArray);
      }

      // Build geometry

      let face = 0;

      for (let c = 0; c < numCoordIndices; ++ face)
      {
         for (let p = 0; p < 6; ++ p, ++ c)
         {
            const
               index      = coordIndex [c],
               { x, y, z} = points [index];

            coordIndicesArray .push (index);

            for (let a = 0; a < numAttribNodes; ++ a)
               attribNodes [a] .addValue (index, attribArrays [a]);

            fogCoordNode ?.addDepth (index, fogDepthArray);

            colorNode ?.addColor (colorPerVertex ? index : face, colorArray);

            if (texCoordNode)
            {
               texCoordNode .addPoint (index, multiTexCoordArray);
            }
            else
            {
               const { x, y } = texCoords [index];

               texCoordArray .push (x, y, 0, 1);
            }

            tangentNode ?.addVector (normalPerVertex ? index : face, tangentArray);
            normalNode  ?.addVector (normalPerVertex ? index : face, normalArray);

            vertexArray .push (x, y, z, 1);
         }
      }

      // Add auto-generated normals if needed.

      if (!normalNode)
      {
         const normals = this .createNormals (points, coordIndex);

         for (const { x, y, z } of normals)
            normalArray .push (x, y, z);
      }

      this .setSolid (this ._solid .getValue ());
      this .setCCW (this ._ccw .getValue ());
   },
});

Object .defineProperties (ElevationGrid,
{
   ... Core_X3DNode .getStaticProperties ("ElevationGrid", "Geometry3D", 3, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_height",      new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "xDimension",      new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "zDimension",      new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "xSpacing",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "zSpacing",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "creaseAngle",     new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "height",          new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const ElevationGrid_default_ = ElevationGrid;
;

/* harmony default export */ const Geometry3D_ElevationGrid = (Namespace/* default */.A .add ("ElevationGrid", ElevationGrid_default_));
;// ./src/x_ite/Components/Geometry3D/Extrusion.js












function Extrusion (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Extrusion);

   // Units

   this ._creaseAngle  .setUnit ("angle");
   this ._crossSection .setUnit ("length");
   this ._spine        .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (Extrusion .prototype, Rendering_X3DGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometryNode .prototype .initialize .call (this);

      this ._set_crossSection .addFieldInterest (this ._crossSection);
      this ._set_orientation  .addFieldInterest (this ._orientation);
      this ._set_scale        .addFieldInterest (this ._scale);
      this ._set_spine        .addFieldInterest (this ._spine);
   },
   getClosed (array)
   {
      if (!array .length)
         return true;

      const
         first = array .at (0)  .getValue (),
         last  = array .at (-1) .getValue ();

      return first .equals (last);
   },
   createPoints: (() =>
   {
      const scale3 = new Numbers_Vector3 ();

      return function ()
      {
         const
            crossSection     = this ._crossSection,
            orientation      = this ._orientation,
            scale            = this ._scale,
            spine            = this ._spine,
            numCrossSections = crossSection .length,
            numOrientations  = orientation .length,
            numScales        = scale .length,
            numSpines        = spine .length,
            points           = [ ];

         // Calculate SCP rotations.

         const rotations = this .createRotations ();

         // Calculate vertices.

         for (let i = 0; i < numSpines; ++ i)
         {
            const matrix = rotations [i];

            if (numOrientations)
               matrix .rotate (orientation [Math .min (i, numOrientations - 1)] .getValue ());

            if (numScales)
            {
               const s = scale [Math .min (i, numScales - 1)] .getValue ();
               matrix .scale (scale3 .set (s .x, 1, s .y));
            }

            for (let cs = 0; cs < numCrossSections; ++ cs)
            {
               const vector = crossSection [cs] .getValue ();
               points .push (matrix .multVecMatrix (new Numbers_Vector3 (vector .x, 0, vector .y)));
            }
         }

         return points;
      };
   })(),
   createRotations: (() =>
   {
      const rotations = [ ];

      const
         SCPxAxis = new Numbers_Vector3 (),
         SCPyAxis = new Numbers_Vector3 (),
         SCPzAxis = new Numbers_Vector3 ();

      const
         SCPyAxisPrevious = new Numbers_Vector3 (),
         SCPzAxisPrevious = new Numbers_Vector3 ();

      const
         vector3  = new Numbers_Vector3 (),
         rotation = new Numbers_Rotation4 ();

      return function ()
      {
         // calculate SCP rotations

         const
            spine       = this ._spine,
            numSpines   = spine .length,
            closedSpine = this .getClosed (spine); // Test only for closed spine.

         // Extend or shrink static rotations array:
         for (let i = rotations .length; i < numSpines; ++ i)
            rotations [i] = new Numbers_Matrix4 ();

         rotations .length = numSpines;

         // SCP axes:
         SCPxAxis .set (0);
         SCPyAxis .set (0);
         SCPzAxis .set (0);

         // SCP for the first point:
         if (closedSpine)
         {
            const s = spine .at (0) .getValue ();

            // Find first defined Y-axis.
            for (let i = 1, length = numSpines - 2; i < length; ++ i)
            {
               SCPyAxis .assign (spine [i] .getValue ()) .subtract (s) .normalize ()
                  .subtract (vector3 .assign (spine [length] .getValue ()) .subtract (s) .normalize ())
                  .normalize ();

               if (!SCPyAxis .equals (Numbers_Vector3 .ZERO))
                  break;
            }

            // Find first defined Z-axis.
            for (let i = 0, length = numSpines - 2; i < length; ++ i)
            {
               SCPzAxis .assign (spine [i + 1] .getValue ()) .subtract (spine [i] .getValue ())
                  .cross (vector3 .assign (spine [length] .getValue ()) .subtract (spine [i] .getValue ()))
                  .normalize ();

               if (!SCPzAxis .equals (Numbers_Vector3 .ZERO))
                  break;
            }
         }
         else
         {
            // Find first defined Y-axis.
            for (let i = 0, length = numSpines - 1; i < length; ++ i)
            {
               SCPyAxis .assign (spine [i + 1] .getValue ()) .subtract (spine [i] .getValue ()) .normalize ();

               if (!SCPyAxis .equals (Numbers_Vector3 .ZERO))
                  break;
            }

            // Find first defined Z-axis.
            for (let i = 1, length = numSpines - 1; i < length; ++ i)
            {
               SCPzAxis .assign (spine [i + 1] .getValue ()) .subtract (spine [i] .getValue ())
                  .cross (vector3 .assign (spine [i - 1] .getValue ()) .subtract (spine [i] .getValue ()))
                  .normalize ();

               if (!SCPzAxis .equals (Numbers_Vector3 .ZERO))
                  break;
            }
         }

         // The entire spine is coincident:
         if (SCPyAxis .equals (Numbers_Vector3 .ZERO))
            SCPyAxis .set (0, 1, 0);

         // The entire spine is collinear:
         if (SCPzAxis .equals (Numbers_Vector3 .ZERO))
            rotation .setVectors (Numbers_Vector3 .Y_AXIS, SCPyAxis) .multVecRot (SCPzAxis .assign (Numbers_Vector3 .Z_AXIS));

         // We do not have to normalize SCPxAxis, as SCPyAxis and SCPzAxis are orthogonal.
         SCPxAxis .assign (SCPyAxis) .cross (SCPzAxis);

         // Get first spine
         const s = spine .at (0) .getValue ();

         rotations [0] .set (... SCPxAxis, 0,
                             ... SCPyAxis, 0,
                             ... SCPzAxis, 0,
                             ... s,        1);

         // For all points other than the first or last:

         SCPyAxisPrevious .assign (SCPyAxis);
         SCPzAxisPrevious .assign (SCPzAxis);

         for (let i = 1, length = numSpines - 1; i < length; ++ i)
         {
            const s = spine [i] .getValue ();

            SCPyAxis .assign (spine [i + 1] .getValue ()) .subtract (s) .normalize ()
               .subtract (vector3 .assign (spine [i - 1] .getValue ()) .subtract (s) .normalize ())
               .normalize ();
            SCPzAxis .assign (spine [i + 1] .getValue ()) .subtract (s)
               .cross (vector3 .assign (spine [i - 1] .getValue ()) .subtract (s))
               .normalize ();

            // Fix edge case.
            // https://www.web3d.org/x3d/content/examples/X3dForAdvancedModeling/GeometricShapes/ExtrusionEdgeCasesIndex.html
            if (SCPyAxisPrevious .dot (SCPyAxis) < 0)
               SCPyAxis .negate ();

            // g.
            if (SCPzAxisPrevious .dot (SCPzAxis) < 0)
               SCPzAxis .negate ();

            // The two points used in computing the Y-axis are coincident.
            if (SCPyAxis .equals (Numbers_Vector3 .ZERO))
               SCPyAxis .assign (SCPyAxisPrevious);
            else
               SCPyAxisPrevious .assign (SCPyAxis);

            // The three points used in computing the Z-axis are collinear.
            if (SCPzAxis .equals (Numbers_Vector3 .ZERO))
               SCPzAxis .assign (SCPzAxisPrevious);
            else
               SCPzAxisPrevious .assign (SCPzAxis);

            // We do not have to normalize SCPxAxis, as SCPyAxis and SCPzAxis are orthogonal.
            SCPxAxis .assign (SCPyAxis) .cross (SCPzAxis);

            rotations [i] .set (... SCPxAxis, 0,
                                ... SCPyAxis, 0,
                                ... SCPzAxis, 0,
                                ... s,        1);
         }

         // SCP for the last point
         if (closedSpine)
         {
            // The SCPs for the first and last points are the same.
            rotations [numSpines - 1] .assign (rotations [0]);
         }
         else
         {
            const s = spine .at (-1) .getValue ();

            SCPyAxis .assign (s) .subtract (spine .at (-2) .getValue ()) .normalize ();

            if (numSpines > 2)
            {
               SCPzAxis .assign (s) .subtract (spine .at (-2) .getValue ())
                  .cross (vector3 .assign (spine .at (-3) .getValue ()) .subtract (spine .at (-2) .getValue ()))
                  .normalize ();
            }

            // Fix edge case.
            // https://www.web3d.org/x3d/content/examples/X3dForAdvancedModeling/GeometricShapes/ExtrusionEdgeCasesIndex.html
            if (SCPyAxisPrevious .dot (SCPyAxis) < 0)
               SCPyAxis .negate ();

            // g.
            if (SCPzAxisPrevious .dot (SCPzAxis) < 0)
               SCPzAxis .negate ();

            // The two points used in computing the Y-axis are coincident.
            if (SCPyAxis .equals (Numbers_Vector3 .ZERO))
               SCPyAxis .assign (SCPyAxisPrevious);

            // The three points used in computing the Z-axis are collinear.
            if (SCPzAxis .equals (Numbers_Vector3 .ZERO))
               SCPzAxis .assign (SCPzAxisPrevious);

            // We do not have to normalize SCPxAxis, as SCPyAxis and SCPzAxis are orthogonal.
            SCPxAxis .assign (SCPyAxis) .cross (SCPzAxis);

            rotations [numSpines - 1] .set (... SCPxAxis, 0,
                                            ... SCPyAxis, 0,
                                            ... SCPzAxis, 0,
                                            ... s,        1);
         }

         return rotations;
      };
   })(),
   build: (() =>
   {
      const
         min     = new Numbers_Vector2 (),
         max     = new Numbers_Vector2 (),
         vector2 = new Numbers_Vector2 ();

      return function ()
      {
         const
            cw                = !this ._ccw .getValue (),
            crossSection      = this ._crossSection,
            spine             = this ._spine,
            numCrossSections  = crossSection .length,
            numSpines         = spine .length,
            coordIndicesArray = this .getCoordIndices (),
            texCoordArray     = this .getTexCoords ();

         if (numSpines < 2 || numCrossSections < 2)
            return;

         this .getMultiTexCoords () .push (texCoordArray);

         const INDEX = (n, k) => n * numCrossSections + k;

         // Use this to determine if start and end points should be connected.
         const closed = this .getClosed (spine)
            && this .getClosed (this ._orientation)
            && this .getClosed (this ._scale);

         const closedCrossSection = this .getClosed (crossSection);

         // For caps calculation

         min .assign (crossSection [0] .getValue ());
         max .assign (crossSection [0] .getValue ());

         for (let k = 1; k < numCrossSections; ++ k)
         {
            min .min (crossSection [k] .getValue ());
            max .max (crossSection [k] .getValue ());
         }

         const
            capSize      = vector2 .assign (max) .subtract (min),
            capMax       = Math .max (capSize .x, capSize .y),
            numCapPoints = closedCrossSection ? numCrossSections - 1 : numCrossSections;

         // Create

         const
            normalIndex = new Map (),
            normals     = [ ],
            points      = this .createPoints (),
            numPoints   = points .length;

         for (let p = 0; p < numPoints; ++ p)
            normalIndex .set (p, [ ]);

         // Build body.

         const
            normalArray = this .getNormals (),
            vertexArray = this .getVertices ();

         const
            numCrossSection_1 = numCrossSections - 1,
            numSpine_1        = numSpines - 1;

         let
            indexLeft  = INDEX (0, 0),
            indexRight = INDEX (0, closedCrossSection ? 0 : numCrossSection_1);

         for (let n = 0; n < numSpine_1; ++ n)
         {
            for (let k = 0; k < numCrossSection_1; ++ k)
            {
               const
                  n1 = closed && n === numSpines - 2 ? 0 : n + 1,
                  k1 = closedCrossSection && k === numCrossSections - 2 ? 0 : k + 1;

               // k      k+1
               //
               // p4 ----- p3   n+1
               //  |     / |
               //  |   /   |
               //  | /     |
               // p1 ----- p2   n

               let
                  i1 = INDEX (n,  k),
                  i2 = INDEX (n,  k1),
                  i3 = INDEX (n1, k1),
                  i4 = INDEX (n1, k),
                  p1 = points [i1],
                  p2 = points [i2],
                  p3 = points [i3],
                  p4 = points [i4],
                  l1 = p2 .distance (p3) >= 1e-7,
                  l2 = p4 .distance (p1) >= 1e-7;

               const
                  normal1 = Geometry_Triangle3 .normal (p1, p2, p3, new Numbers_Vector3 ()),
                  normal2 = Geometry_Triangle3 .normal (p1, p3, p4, new Numbers_Vector3 ());

               if (cw)
               {
                  normal1 .negate ();
                  normal2 .negate ();
               }

               // Merge points on the left and right side if spine is coincident for better normal generation.

               if (k === 0)
               {
                  if (l2)
                  {
                     indexLeft = i1;
                  }
                  else
                  {
                     i1 = indexLeft;
                     p1 = points [i1];
                  }
               }

               if (k === numCrossSections - 2)
               {
                  if (l1)
                  {
                     indexRight = i2;
                  }
                  else
                  {
                     i3 = indexRight;
                     p3 = points [i3];
                  }
               }

               // If there are coincident spine points then one length can be zero.

               // Triangle one

               if (l1)
               {
                  coordIndicesArray .push (i1, i2, i3);

                  // p1
                  if (l2)
                  {
                     texCoordArray .push (k / numCrossSection_1, n / numSpine_1, 0, 1);
                  }
                  else
                  {
                     // Cone case: ((texCoord1 + texCoord4) / 2)
                     const y = (n / numSpine_1 + (n + 1) / numSpine_1) / 2;

                     texCoordArray .push (k / numCrossSection_1, y, 0, 1);
                  }

                  normalIndex .get (i1) .push (normals .length);
                  normals .push (normal1);
                  vertexArray .push (... p1, 1);

                  // p2
                  texCoordArray .push ((k + 1) / numCrossSection_1, n / numSpine_1, 0, 1);
                  normalIndex .get (i2) .push (normals .length);
                  normals .push (normal1);
                  vertexArray .push (... p2, 1);

                  // p3
                  texCoordArray .push ((k + 1) / numCrossSection_1, (n + 1) / numSpine_1, 0, 1);
                  normalIndex .get (i3) .push (normals .length);
                  normals .push (normal1);
                  vertexArray .push (... p3, 1);
               }

               // Triangle two

               if (l2)
               {
                  coordIndicesArray .push (i1, i3, i4);

                  // p1
                  texCoordArray .push (k / numCrossSection_1, n / numSpine_1, 0, 1);
                  normalIndex .get (i1) .push (normals .length);
                  normals .push (normal2);
                  vertexArray .push (... p1, 1);

                  // p3
                  if (l1)
                  {
                     texCoordArray .push ((k + 1) / numCrossSection_1, (n + 1) / numSpine_1, 0, 1);
                  }
                  else
                  {
                     // Cone case: ((texCoord3 + texCoord2) / 2)
                     const y = ((n + 1) / numSpine_1 + n / numSpine_1) / 2;

                     texCoordArray .push ((k + 1) / numCrossSection_1, y, 0, 1);
                  }

                  normalIndex .get (i3) .push (normals .length);
                  normals .push (normal2);
                  vertexArray .push (... p3, 1);

                  // p4
                  texCoordArray .push (k / numCrossSection_1, (n + 1) / numSpine_1, 0, 1);
                  normalIndex .get (i4) .push (normals .length);
                  normals .push (normal2);
                  vertexArray .push (... p4, 1);
               }
            }
         }

         // Refine body normals and add them.

         const refinedNormals = this .refineNormals (normalIndex, normals, this ._creaseAngle .getValue ());

         for (const { x, y, z } of refinedNormals)
            normalArray .push (x, y, z);

         // Build caps

         if (capMax && numCapPoints > 2)
         {
            if (this ._beginCap .getValue ())
            {
               const
                  j         = 0, // spine
                  convex    = this ._convex .getValue (),
                  texCoord  = { },
                  polygon   = [ ],
                  triangles = [ ];

               for (let k = 0; k < numCapPoints; ++ k)
               {
                  const
                     index = INDEX (j, numCapPoints - 1 - k),
                     point = points [index] .copy ();

                  point .index     = index;
                  texCoord [index] = crossSection [numCapPoints - 1 - k] .getValue () .copy () .subtract (min) .divide (capMax);
                  polygon .push (convex ? index : point);
               }

               if (convex)
                  Geometry_Triangle3 .triangulateConvexPolygon (polygon, triangles);

               else
                  Geometry_Triangle3 .triangulatePolygon (polygon, triangles);

               if (triangles .length >= 3)
               {
                  const normal = Geometry_Triangle3 .normal (points [triangles [0]],
                                                    points [triangles [1]],
                                                    points [triangles [2]],
                                                    new Numbers_Vector3 ());

                  if (cw)
                     normal .negate ();

                  this .addCap (texCoordArray, texCoord, normal, points, triangles);
               }
            }

            if (this ._endCap .getValue ())
            {
               const
                  j         = numSpines - 1, // spine
                  convex    = this ._convex .getValue (),
                  texCoord  = { },
                  polygon   = [ ],
                  triangles = [ ];

               for (let k = 0; k < numCapPoints; ++ k)
               {
                  const
                     index = INDEX (j, k),
                     point = points [index] .copy ();

                  point .index     = index;
                  texCoord [index] = crossSection [k] .getValue () .copy () .subtract (min) .divide (capMax);
                  polygon .push (convex ? index : point);
               }

               if (convex)
                  Geometry_Triangle3 .triangulateConvexPolygon (polygon, triangles);

               else
                  Geometry_Triangle3 .triangulatePolygon (polygon, triangles);

               if (triangles .length >= 3)
               {
                  const normal = Geometry_Triangle3 .normal (points [triangles [0]],
                                                    points [triangles [1]],
                                                    points [triangles [2]],
                                                    new Numbers_Vector3 ());

                  if (cw)
                     normal .negate ();

                  this .addCap (texCoordArray, texCoord, normal, points, triangles);
               }
            }
         }

         this .setSolid (this ._solid .getValue ());
         this .setCCW (this ._ccw .getValue ());
      };
   })(),
   addCap (texCoordArray, texCoord, normal, vertices, triangles)
   {
      const
         coordIndicesArray = this .getCoordIndices (),
         normalArray       = this .getNormals (),
         vertexArray       = this .getVertices (),
         numTriangles      = triangles .length;

      for (let i = 0; i < numTriangles; i += 3)
      {
         const
            i1 = triangles [i],
            i2 = triangles [i + 1],
            i3 = triangles [i + 2],
            p1 = vertices [i1],
            p2 = vertices [i2],
            p3 = vertices [i3],
            t0 = texCoord [i1],
            t1 = texCoord [i2],
            t2 = texCoord [i3];

         coordIndicesArray .push (i1, i2, i3);

         texCoordArray .push (... t0, 0, 1, ... t1, 0, 1, ... t2, 0, 1);
         normalArray   .push (... normal,   ... normal,   ... normal);
         vertexArray   .push (... p1, 1,    ... p2, 1,    ... p3, 1);
      }
   },
});

Object .defineProperties (Extrusion,
{
   ... Core_X3DNode .getStaticProperties ("Extrusion", "Geometry3D", 4, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_crossSection", new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_orientation",  new x_ite_Fields .MFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_scale",        new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_spine",        new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "beginCap",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "endCap",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "convex",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "creaseAngle",      new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "crossSection",     new x_ite_Fields .MFVec2f (new Numbers_Vector2 (1), new Numbers_Vector2 (1, -1), new Numbers_Vector2 (-1, -1), new Numbers_Vector2 (-1, 1), new Numbers_Vector2 (1))),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "orientation",      new x_ite_Fields .MFRotation (new Numbers_Rotation4 ())),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "scale",            new x_ite_Fields .MFVec2f (new Numbers_Vector2 (1))),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "spine",            new x_ite_Fields .MFVec3f (new Numbers_Vector3 (), new Numbers_Vector3 (0, 1, 0))),
      ]),
      enumerable: true,
   },
});

const Extrusion_default_ = Extrusion;
;

/* harmony default export */ const Geometry3D_Extrusion = (Namespace/* default */.A .add ("Extrusion", Extrusion_default_));
;// ./src/x_ite/Components/Geometry3D/Sphere.js







function Sphere (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Sphere);

   // Units

   this ._radius .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (Sphere .prototype, Rendering_X3DGeometryNode .prototype),
{
   set_live__ ()
   {
      this .connectOptions (this .getBrowser () .getSphereOptions ());
   },
   build ()
   {
      const
         options     = this .getBrowser () .getSphereOptions (),
         geometry    = options .getGeometry (),
         radius      = Math .abs (this ._radius .getValue ()),
         vertexArray = this .getVertices ();

      this .getMultiTexCoords () .push (... geometry .getMultiTexCoords ());
      this .getTangents () .assign (geometry .getTangents ());
      this .getNormals ()  .assign (geometry .getNormals ());

      if (radius === 1)
      {
         vertexArray .assign (geometry .getVertices ());
      }
      else
      {
         const defaultVertices = geometry .getVertices ();

         for (let i = 0, length = defaultVertices .length; i < length; i += 4)
         {
            vertexArray .push (radius * defaultVertices [i],
                               radius * defaultVertices [i + 1],
                               radius * defaultVertices [i + 2],
                               1);
         }
      }

      this .getMin () .set (-radius, -radius, -radius);
      this .getMax () .set ( radius,  radius,  radius);

      this .setSolid (this ._solid .getValue ());
   },
});

Object .defineProperties (Sphere,
{
   ... Core_X3DNode .getStaticProperties ("Sphere", "Geometry3D", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "radius",   new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",    new x_ite_Fields .SFBool (true)),
      ]),
      enumerable: true,
   },
});

const Sphere_default_ = Sphere;
;

/* harmony default export */ const Geometry3D_Sphere = (Namespace/* default */.A .add ("Sphere", Sphere_default_));
;// ./src/x_ite/Components/Geometry3DComponent.js








const Geometry3DComponent_default_ = {
   name: "Geometry3D",
   concreteNodes:
   [
      Geometry3D_Box,
      Geometry3D_Cone,
      Geometry3D_Cylinder,
      Geometry3D_ElevationGrid,
      Geometry3D_Extrusion,
      Geometry3D_IndexedFaceSet,
      Geometry3D_Sphere,
   ],
   abstractNodes:
   [
   ],
};
;

/* harmony default export */ const Geometry3DComponent = (Namespace/* default */.A .add ("Geometry3DComponent", Geometry3DComponent_default_));
;// ./src/x_ite/Components/Grouping/X3DTransformMatrix3DNode.js







function X3DTransformMatrix3DNode (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTransformMatrix3DNode);

   this .matrix = new Numbers_Matrix4 ();
}

Object .assign (Object .setPrototypeOf (X3DTransformMatrix3DNode .prototype, Grouping_X3DGroupingNode .prototype),
{
   getBBox (bbox, shadows)
   {
      if (this .isDefaultBBoxSize ())
         return this .getSubBBox (bbox, shadows) .multRight (this .matrix);

      return bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
   },
   getShapes (shapes, parentModelMatrix)
   {
      const modelMatrix = parentModelMatrix .copy () .multLeft (this .matrix);

      return Grouping_X3DGroupingNode .prototype .getShapes .call (this, shapes, modelMatrix);
   },
   getMatrix ()
   {
      return this .matrix;
   },
   setMatrix (matrix)
   {
      if (matrix .equals (Numbers_Matrix4 .IDENTITY))
      {
         this .matrix .set ();

         if (this .isDefaultBBoxSize ())
            this .getBBox = this .getSubBBox;
         else
            delete this .getBBox;

         this .traverse = this .groupTraverse;
      }
      else
      {
         this .matrix .assign (matrix);

         delete this .getBBox;
         delete this .traverse;
      }

      this .getExecutionContext () ._bbox_changed = Date .now () / 1000;
   },
   setTransform (t, r, s, so, c)
   {
      if (t .equals (Numbers_Vector3 .ZERO) && r .equals (Numbers_Rotation4 .IDENTITY) && s .equals (Numbers_Vector3 .ONE))
      {
         this .matrix .set ();

         if (this .isDefaultBBoxSize ())
            this .getBBox = this .getSubBBox;
         else
            delete this .getBBox;

         this .traverse = this .groupTraverse;
      }
      else
      {
         this .matrix .setTransform (t, r, s, so, c);

         delete this .getBBox;
         delete this .traverse;
      }

      this .getExecutionContext () ._bbox_changed = Date .now () / 1000;
   },
   traverse (type, renderObject)
   {
      const
         path            = renderObject .getPath (),
         modelViewMatrix = renderObject .getModelViewMatrix ();

      path .push (this .getId ());
      modelViewMatrix .push ();
      modelViewMatrix .multLeft (this .matrix);

      Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);

      modelViewMatrix .pop ();
      path .pop ();
   },
   groupTraverse: Grouping_X3DGroupingNode .prototype .traverse,
});

Object .defineProperties (X3DTransformMatrix3DNode, Core_X3DNode .getStaticProperties ("X3DTransformMatrix3DNode", "Grouping", 1));

const X3DTransformMatrix3DNode_default_ = X3DTransformMatrix3DNode;
;

/* harmony default export */ const Grouping_X3DTransformMatrix3DNode = (Namespace/* default */.A .add ("X3DTransformMatrix3DNode", X3DTransformMatrix3DNode_default_));
;// ./src/x_ite/Components/Grouping/X3DTransformNode.js




function X3DTransformNode (executionContext)
{
   Grouping_X3DTransformMatrix3DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTransformNode);

   // Units

   this ._translation .setUnit ("length");
   this ._center      .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (X3DTransformNode .prototype, Grouping_X3DTransformMatrix3DNode .prototype),
{
   initialize ()
   {
      Grouping_X3DTransformMatrix3DNode .prototype .initialize .call (this);

      this .addInterest ("eventsProcessed", this);

      this .eventsProcessed ();
   },
   eventsProcessed ()
   {
      this .setTransform (this ._translation      .getValue (),
                          this ._rotation         .getValue (),
                          this ._scale            .getValue (),
                          this ._scaleOrientation .getValue (),
                          this ._center           .getValue ());
   },
});

Object .defineProperties (X3DTransformNode, Core_X3DNode .getStaticProperties ("X3DTransformNode", "Grouping", 1));

const X3DTransformNode_default_ = X3DTransformNode;
;

/* harmony default export */ const Grouping_X3DTransformNode = (Namespace/* default */.A .add ("X3DTransformNode", X3DTransformNode_default_));
;// ./src/x_ite/Components/Grouping/Transform.js







function Transform (executionContext)
{
   Grouping_X3DTransformNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Transform);
}

Object .setPrototypeOf (Transform .prototype, Grouping_X3DTransformNode .prototype);

Object .defineProperties (Transform,
{
   ... Core_X3DNode .getStaticProperties ("Transform", "Grouping", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "translation",      new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "rotation",         new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "scale",            new x_ite_Fields .SFVec3f (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "scaleOrientation", new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "center",           new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",         new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",       new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",      new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren",   new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",         new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Transform_default_ = Transform;
;

/* harmony default export */ const Grouping_Transform = (Namespace/* default */.A .add ("Transform", Transform_default_));
;// ./src/x_ite/Components/Rendering/X3DPointGeometryNode.js



function X3DPointGeometryNode (executionContext)
{
   Rendering_X3DGeometryNode .call (this, executionContext);

   this .setGeometryType (0);
   this .setSolid (false);
}

Object .assign (Object .setPrototypeOf (X3DPointGeometryNode .prototype, Rendering_X3DGeometryNode .prototype),
{
   intersectsLine ()
   {
      return false;
   },
   generateTexCoords ()
   { },
   displaySimple (gl, renderContext, shaderNode)
   {
      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         if (this .multiTexCoords .length)
            shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
      }

      gl .drawArrays (gl .POINTS, 0, this .vertexCount);
   },
   display (gl, renderContext)
   {
      const
         { viewport, appearanceNode, transparent } = renderContext,
         browser         = this .getBrowser (),
         shaderNode      = appearanceNode .getShader (this, renderContext),
         renderModeNodes = appearanceNode .getRenderModes ();

      // Enable sample alpha to coverage if not transparent.

      if (!transparent)
      {
         gl .enable (gl .SAMPLE_ALPHA_TO_COVERAGE);
         gl .colorMask (true, true, true, false);
      }

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Setup shader.

      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this);

      // Setup vertex attributes.

      if (this .vertexArrayObject .enable (shaderNode .getProgram ()))
      {
         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (this .colorMaterial)
            shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);

         if (this .hasTangents)
            shaderNode .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);
      }

      gl .drawArrays (gl .POINTS, 0, this .vertexCount);

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();

      // Disable sample alpha to coverage if not transparent.

      if (!transparent)
      {
         gl .disable (gl .SAMPLE_ALPHA_TO_COVERAGE);
         gl .colorMask (true, true, true, true);
      }
   },
   displaySimpleInstanced (gl, shaderNode, shapeNode)
   {
      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, particleOffset, matrixOffset, normalMatrixOffset } = shapeNode;

         if (particleOffset !== undefined)
            shaderNode .enableParticleAttribute (gl, instances, instancesStride, particleOffset, 1);

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         shaderNode .enableTexCoordAttribute (gl, this .texCoordBuffers, 0, 0);
         shaderNode .enableNormalAttribute   (gl, this .normalBuffer,    0, 0);
         shaderNode .enableVertexAttribute   (gl, this .vertexBuffer,    0, 0);

         this .updateInstances = false;
      }

      gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount, shapeNode .getNumInstances ());
   },
   displayInstanced (gl, renderContext, shapeNode)
   {
      const
         { viewport, appearanceNode, transparent } = renderContext,
         browser         = this .getBrowser (),
         shaderNode      = appearanceNode .getShader (this, renderContext),
         renderModeNodes = appearanceNode .getRenderModes ();

      // Enable sample alpha to coverage if not transparent.

      if (!transparent)
      {
         gl .enable (gl .SAMPLE_ALPHA_TO_COVERAGE);
         gl .colorMask (true, true, true, false);
      }

      // Set viewport.

      gl .viewport (... viewport);
      gl .scissor (... viewport);

      // Enable render mode nodes.

      for (const node of renderModeNodes)
         node .enable (gl);

      // Setup shader.

      shaderNode .enable (gl);
      shaderNode .setUniforms (gl, renderContext, this);

      // Setup vertex attributes.

      const instances = shapeNode .getInstances ();

      if (instances .vertexArrayObject .update (this .updateInstances) .enable (shaderNode .getProgram ()))
      {
         const { instancesStride, particleOffset, velocityOffset, matrixOffset, normalMatrixOffset } = shapeNode;

         const
            attribNodes   = this .getAttrib (),
            attribBuffers = this .getAttribBuffers ();

         if (particleOffset !== undefined)
            shaderNode .enableParticleAttribute (gl, instances, instancesStride, particleOffset, 1);

         if (velocityOffset !== undefined)
            shaderNode .enableParticleVelocityAttribute (gl, instances, instancesStride, velocityOffset, 1);

         shaderNode .enableInstanceMatrixAttribute (gl, instances, instancesStride, matrixOffset, 1);

         if (normalMatrixOffset !== undefined)
            shaderNode .enableInstanceNormalMatrixAttribute (gl, instances, instancesStride, normalMatrixOffset, 1);

         if (this .coordIndices .length)
            shaderNode .enableCoordIndexAttribute (gl, this .coordIndexBuffer, 0, 0);

         for (let i = 0, length = attribNodes .length; i < length; ++ i)
            attribNodes [i] .enable (gl, shaderNode, attribBuffers [i]);

         if (this .hasFogCoords)
            shaderNode .enableFogDepthAttribute (gl, this .fogDepthBuffer, 0, 0);

         if (this .colorMaterial)
            shaderNode .enableColorAttribute (gl, this .colorBuffer, 0, 0);

         if (this .hasTangents)
            shaderNode .enableTangentAttribute (gl, this .tangentBuffer, 0, 0);

         if (this .hasNormals)
            shaderNode .enableNormalAttribute (gl, this .normalBuffer, 0, 0);

         shaderNode .enableVertexAttribute (gl, this .vertexBuffer, 0, 0);

         this .updateInstances = false;
      }

      // Wireframes are always solid so only one drawing call is needed.

      gl .drawArraysInstanced (gl .POINTS, 0, this .vertexCount, shapeNode .getNumInstances ());

      // Disable render mode nodes.

      for (const node of renderModeNodes)
         node .disable (gl);

      // Reset texture units.

      browser .resetTextureUnits ();

      // Disable sample alpha to coverage if not transparent.

      if (!transparent)
      {
         gl .disable (gl .SAMPLE_ALPHA_TO_COVERAGE);
         gl .colorMask (true, true, true, true);
      }
   },
});

Object .defineProperties (X3DPointGeometryNode, Core_X3DNode .getStaticProperties ("X3DPointGeometryNode", "Rendering", 1));

const X3DPointGeometryNode_default_ = X3DPointGeometryNode;
;

/* harmony default export */ const Rendering_X3DPointGeometryNode = (Namespace/* default */.A .add ("X3DPointGeometryNode", X3DPointGeometryNode_default_));
;// ./src/x_ite/Components/Rendering/PointSet.js







function PointSet (executionContext)
{
   Rendering_X3DPointGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PointSet);
}

Object .assign (Object .setPrototypeOf (PointSet .prototype, Rendering_X3DPointGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DPointGeometryNode .prototype .initialize .call (this);

      this ._attrib   .addInterest ("set_attrib__",   this);
      this ._fogCoord .addInterest ("set_fogCoord__", this);
      this ._color    .addInterest ("set_color__",    this);
      this ._tangent  .addInterest ("set_tangent__",  this);
      this ._normal   .addInterest ("set_normal__",   this);
      this ._coord    .addInterest ("set_coord__",    this);

      this .set_attrib__ ();
      this .set_fogCoord__ ();
      this .set_color__ ();
      this .set_tangent__ ();
      this .set_normal__ ();
      this .set_coord__ ();
   },
   build ()
   {
      if (!this .coordNode ?.getSize ())
         return;

      const
         coordIndicesArray = this .getCoordIndices (),
         attribNodes       = this .getAttrib (),
         numAttribNodes    = attribNodes .length,
         attribArrays      = this .getAttribs (),
         fogCoordNode      = this .getFogCoord (),
         fogDepthArray     = this .getFogDepths (),
         colorNode         = this .getColor (),
         colorArray        = this .getColors (),
         tangentNode       = this .getTangent (),
         normalNode        = this .getNormal (),
         coordNode         = this .getCoord (),
         tangentArray      = this .getTangents (),
         normalArray       = this .getNormals (),
         vertexArray       = this .getVertices (),
         numPoints         = coordNode ._point .length;

      for (let i = 0; i < numPoints; ++ i)
         coordIndicesArray .push (i);

      for (let a = 0; a < numAttribNodes; ++ a)
      {
         for (let i = 0; i < numPoints; ++ i)
            attribNodes [a] .addValue (i, attribArrays [a]);
      }

      fogCoordNode ?.addDepths  (fogDepthArray, numPoints);
      colorNode    ?.addColors  (colorArray,    numPoints);
      tangentNode  ?.addVectors (tangentArray,  numPoints);
      normalNode   ?.addVectors (normalArray,   numPoints);

      coordNode .addPoints (vertexArray);
   },
});

Object .defineProperties (PointSet,
{
   ... Core_X3DNode .getStaticProperties ("PointSet", "Rendering", 1, "geometry", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "attrib",   new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fogCoord", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tangent",  new x_ite_Fields .SFNode ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normal",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "coord",    new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const PointSet_default_ = PointSet;
;

/* harmony default export */ const Rendering_PointSet = (Namespace/* default */.A .add ("PointSet", PointSet_default_));
;// ./src/x_ite/Components/Rendering/LineSet.js







function LineSet (executionContext)
{
   Rendering_X3DLineGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .LineSet);
}

Object .assign (Object .setPrototypeOf (LineSet .prototype, Rendering_X3DLineGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DLineGeometryNode .prototype .initialize .call (this);

      this ._attrib   .addInterest ("set_attrib__",   this);
      this ._fogCoord .addInterest ("set_fogCoord__", this);
      this ._color    .addInterest ("set_color__",    this);
      this ._tangent  .addInterest ("set_tangent__",  this);
      this ._normal   .addInterest ("set_normal__",   this);
      this ._coord    .addInterest ("set_coord__",    this);

      this .set_attrib__ ();
      this .set_fogCoord__ ();
      this .set_color__ ();
      this .set_tangent__ ();
      this .set_normal__ ();
      this .set_coord__ ();
   },
   build ()
   {
      if (!this .coordNode ?.getSize ())
         return;

      // Fill GeometryNode

      const
         vertexCount       = this ._vertexCount,
         coordIndicesArray = this .getCoordIndices (),
         attribNodes       = this .getAttrib (),
         numAttribNodes    = attribNodes .length,
         attribArrays      = this .getAttribs (),
         fogCoordNode      = this .getFogCoord (),
         colorNode         = this .getColor (),
         tangentNode       = this .getTangent (),
         normalNode        = this .getNormal (),
         coordNode         = this .getCoord (),
         fogDepthArray     = this .getFogDepths (),
         colorArray        = this .getColors (),
         tangentArray      = this .getTangents (),
         normalArray       = this .getNormals (),
         vertexArray       = this .getVertices (),
         size              = coordNode .getSize ();

      let index = 0;

      for (let count of vertexCount)
      {
         if (index + count > size)
            break;

         if (count > 1)
         {
            count = 2 * count - 2; // numVertices for line strip

            for (let i = 0; i < count; ++ i, index += i & 1)
            {
               coordIndicesArray .push (index);

               for (let a = 0; a < numAttribNodes; ++ a)
                  attribNodes [a] .addValue (index, attribArrays [a]);

               fogCoordNode ?.addDepth  (index, fogDepthArray);
               colorNode    ?.addColor  (index, colorArray);
               tangentNode  ?.addVector (index, tangentArray);
               normalNode   ?.addVector (index, normalArray);

               coordNode .addPoint (index, vertexArray);
            }

            ++ index;
         }
         else
            index += count;
      }
   },
});

Object .defineProperties (LineSet,
{
   ... Core_X3DNode .getStaticProperties ("LineSet", "Rendering", 1, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "vertexCount", new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "attrib",      new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fogCoord",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tangent",     new x_ite_Fields .SFNode ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normal",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "coord",       new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const LineSet_default_ = LineSet;
;

/* harmony default export */ const Rendering_LineSet = (Namespace/* default */.A .add ("LineSet", LineSet_default_));
;// ./src/x_ite/Components/Rendering/TriangleSet.js







function TriangleSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TriangleSet);
}

Object .assign (Object .setPrototypeOf (TriangleSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this .getCoord () ?.getSize () ?? 0, 3);
   },
   build ()
   {
      const length = this .getNumVertices ();

      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, length, 3, length);
   },
   createNormals (verticesPerPolygon, polygonsSize, polygons)
   {
      return this .createFaceNormals (verticesPerPolygon, polygonsSize, polygons);
   },
});

Object .defineProperties (TriangleSet,
{
   ... Core_X3DNode .getStaticProperties ("TriangleSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const TriangleSet_default_ = TriangleSet;
;

/* harmony default export */ const Rendering_TriangleSet = (Namespace/* default */.A .add ("TriangleSet", TriangleSet_default_));
;// ./src/x_ite/Components/Rendering/X3DTangentNode.js




function X3DTangentNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTangentNode);
}

Object .setPrototypeOf (X3DTangentNode .prototype, Rendering_X3DGeometricPropertyNode .prototype);

Object .defineProperties (X3DTangentNode, Core_X3DNode .getStaticProperties ("X3DTangentNode", "Rendering", 5));

const X3DTangentNode_default_ = X3DTangentNode;
;

/* harmony default export */ const Rendering_X3DTangentNode = (Namespace/* default */.A .add ("X3DTangentNode", X3DTangentNode_default_));
;// ./src/x_ite/Components/Rendering/Tangent.js







/**
 * THIS NODE IS STILL EXPERIMENTAL.
 */

function Tangent (executionContext)
{
   Rendering_X3DTangentNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Tangent);
}

Object .assign (Object .setPrototypeOf (Tangent .prototype, Rendering_X3DTangentNode .prototype),
{
   initialize ()
   {
      Rendering_X3DTangentNode .prototype .initialize .call (this);

      this ._vector .addInterest ("set_vector__", this);

      this .set_vector__ ();
   },
   set_vector__ ()
   {
      this .vector = this ._vector .getValue ();
      this .length = this ._vector .length;
   },
   addVector (index, array)
   {
      if (index >= 0 && this .length)
      {
         const
            vector = this .vector,
            i      = (index % this .length) * 4;

         array .push (vector [i], vector [i + 1], vector [i + 2], vector [i + 3]);
      }
      else
      {
         return array .push (1, 0, 0, 1);
      }
   },
   addVectors (array, min = this .length)
   {
      const length = this .length;

      if (length)
      {
         const vector = this .vector;

         for (let index = 0; index < min; ++ index)
         {
            const i = (index % length) * 4;

            array .push (vector [i], vector [i + 1], vector [i + 2], vector [i + 3]);
         }
      }
      else
      {
         for (let index = 0; index < min; ++ index)
            array .push (1, 0, 0, 1);
      }

      return array;
   },
});

Object .defineProperties (Tangent,
{
   // X_ITE introduces this node in 4.0, but X3D specification adds this node in 4.1.
   ... Core_X3DNode .getStaticProperties ("Tangent", "Rendering", 5, "tangent", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "vector",   new x_ite_Fields .MFVec4f ()),
      ]),
      enumerable: true,
   },
});

const Tangent_default_ = Tangent;
;

/* harmony default export */ const Rendering_Tangent = (Namespace/* default */.A .add ("Tangent", Tangent_default_));
;// ./src/x_ite/Components/Rendering/X3DColorNode.js





function X3DColorNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DColorNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (X3DColorNode .prototype, Rendering_X3DGeometricPropertyNode .prototype),
{
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
});

Object .defineProperties (X3DColorNode, Core_X3DNode .getStaticProperties ("X3DColorNode", "Rendering", 1));

const X3DColorNode_default_ = X3DColorNode;
;

/* harmony default export */ const Rendering_X3DColorNode = (Namespace/* default */.A .add ("X3DColorNode", X3DColorNode_default_));
;// ./src/x_ite/Components/Rendering/Color.js







function Color (executionContext)
{
   Rendering_X3DColorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Color);
}

Object .assign (Object .setPrototypeOf (Color .prototype, Rendering_X3DColorNode .prototype),
{
   initialize ()
   {
      Rendering_X3DColorNode .prototype .initialize .call (this);

      this ._color .addInterest ("set_color__", this);

      this .set_color__ ();
   },
   set_color__ ()
   {
      this .color  = this ._color .getValue ();
      this .length = this ._color .length;
   },
   addColor (index, array)
   {
      if (index >= 0 && this .length)
      {
         const
            color = this .color,
            i     = (index % this .length) * 3;

         return array .push (color [i], color [i + 1], color [i + 2], 1);
      }
      else
      {
         array .push (1, 1, 1, 1);
      }
   },
   addColors (array, min = this .length)
   {
      const length = this .length;

      if (length)
      {
         const color = this .color;

         for (let index = 0; index < min; ++ index)
         {
            const i = (index % length) * 3;

            array .push (color [i], color [i + 1], color [i + 2], 1);
         }
      }
      else
      {
         for (let index = 0; index < min; ++ index)
            array .push (1, 1, 1, 1);
      }

      return array;
   },
});

Object .defineProperties (Color,
{
   ... Core_X3DNode .getStaticProperties ("Color", "Rendering", 1, "color", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",    new x_ite_Fields .MFColor ()),
      ]),
      enumerable: true,
   },
});

const Color_default_ = Color;
;

/* harmony default export */ const Rendering_Color = (Namespace/* default */.A .add ("Color", Color_default_));
;// ./src/x_ite/Components/Rendering/ColorRGBA.js







function ColorRGBA (executionContext)
{
   Rendering_X3DColorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ColorRGBA);

   this .setTransparent (true);
}

Object .assign (Object .setPrototypeOf (ColorRGBA .prototype, Rendering_X3DColorNode .prototype),
{
   initialize ()
   {
      Rendering_X3DColorNode .prototype .initialize .call (this);

      this ._color .addInterest ("set_color__", this);

      this .set_color__ ();
   },
   set_color__ ()
   {
      this .color  = this ._color .getValue ();
      this .length = this ._color .length;
   },
   addColor (index, array)
   {
      if (index >= 0 && this .length)
      {
         const
            color = this .color,
            i     = (index % this .length) * 4;

         return array .push (color [i], color [i + 1], color [i + 2], color [i + 3]);
      }
      else
      {
         array .push (1, 1, 1, 1);
      }
   },
   addColors (array, min = this .length)
   {
      const length = this .length;

      if (length)
      {
         const color = this .color;

         for (let index = 0; index < min; ++ index)
         {
            const i = (index % length) * 4;

            array .push (color [i], color [i + 1], color [i + 2], color [i + 3]);
         }
      }
      else
      {
         for (let index = 0; index < min; ++ index)
            array .push (1, 1, 1, 1);
      }

      return array;
   },
});

Object .defineProperties (ColorRGBA,
{
   ... Core_X3DNode .getStaticProperties ("ColorRGBA", "Rendering", 1, "color", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",    new x_ite_Fields .MFColorRGBA ()),
      ]),
      enumerable: true,
   },
});

const ColorRGBA_default_ = ColorRGBA;
;

/* harmony default export */ const Rendering_ColorRGBA = (Namespace/* default */.A .add ("ColorRGBA", ColorRGBA_default_));
;// ./src/x_ite/Components/Texturing/MultiTextureCoordinate.js








function MultiTextureCoordinate (executionContext)
{
   Texturing_X3DTextureCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .MultiTextureCoordinate);

   // Private properties

   const browser = this .getBrowser ();

   this .maxTexCoords           = browser .getMaxTexCoords ();
   this .textureCoordinateNodes = [ ];
}

Object .assign (Object .setPrototypeOf (MultiTextureCoordinate .prototype, Texturing_X3DTextureCoordinateNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTextureCoordinateNode .prototype .initialize .call (this);

      this ._texCoord .addInterest ("set_texCoord__", this);

      this .set_texCoord__ ();
   },
   set_texCoord__ ()
   {
      const textureCoordinateNodes = this .textureCoordinateNodes;

      for (const textureCoordinateNode of textureCoordinateNodes)
         textureCoordinateNode .removeInterest ("addNodeEvent", this);

      textureCoordinateNodes .length = 0;

      for (const node of this ._texCoord)
      {
         const textureCoordinateNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureCoordinateNode, node);

         if (textureCoordinateNode)
            textureCoordinateNodes .push (textureCoordinateNode);
      }

      for (const textureCoordinateNode of textureCoordinateNodes)
         textureCoordinateNode .addInterest ("addNodeEvent", this);
   },
   getCount ()
   {
      return Math .min (this .maxTexCoords, this .textureCoordinateNodes .length);
   },
   init (multiArray)
   {
      const
         textureCoordinateNodes = this .textureCoordinateNodes,
         length                 = Math .min (this .maxTexCoords, textureCoordinateNodes .length);

      for (let i = 0; i < length; ++ i)
         textureCoordinateNodes [i] .init (multiArray);
   },
   addPoint (index, multiArray)
   {
      const
         textureCoordinateNodes = this .textureCoordinateNodes,
         length                 = Math .min (this .maxTexCoords, textureCoordinateNodes .length);

      for (let i = 0; i < length; ++ i)
         textureCoordinateNodes [i] .addPointToChannel (index, multiArray [i]);
   },
   addPoints (array)
   {
      for (const textureCoordinateNode of this .textureCoordinateNodes)
         return textureCoordinateNode .addPoints (array);

      return array;
   },
   getTextureCoordinates ()
   {
      return this .textureCoordinateNodes;
   },
   getTextureCoordinateMapping (textureCoordinateMapping)
   {
      const
         textureCoordinateNodes = this .textureCoordinateNodes,
         length                 = Math .min (this .maxTexCoords, textureCoordinateNodes .length);

      for (let i = 0; i < length; ++ i)
         textureCoordinateNodes [i] .getTextureCoordinateMapping (textureCoordinateMapping, i);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         textureCoordinateNodes = this .textureCoordinateNodes,
         length                 = Math .min (this .maxTexCoords, textureCoordinateNodes .length);

      if (length)
      {
         for (let i = 0; i < length; ++ i)
            textureCoordinateNodes [i] .setShaderUniforms (gl, shaderObject, i);
      }
      else
      {
         this .getBrowser () .getDefaultTextureCoordinate () .setShaderUniforms (gl, shaderObject, 0);
      }
   },
});

Object .defineProperties (MultiTextureCoordinate,
{
   ... Core_X3DNode .getStaticProperties ("MultiTextureCoordinate", "Texturing", 2, "texCoord", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "texCoord", new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const MultiTextureCoordinate_default_ = MultiTextureCoordinate;
;

/* harmony default export */ const Texturing_MultiTextureCoordinate = (Namespace/* default */.A .add ("MultiTextureCoordinate", MultiTextureCoordinate_default_));
;// ./src/x_ite/Components/Texturing/TextureCoordinateGenerator.js








function TextureCoordinateGenerator (executionContext)
{
   Texturing_X3DSingleTextureCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TextureCoordinateGenerator);

   // Private properties

   this .mode      = TextureCoordinateGeneratorModeType .SPHERE;
   this .parameter = new Float32Array (6);
}

Object .assign (Object .setPrototypeOf (TextureCoordinateGenerator .prototype, Texturing_X3DSingleTextureCoordinateNode .prototype),
{
   initialize ()
   {
      Texturing_X3DSingleTextureCoordinateNode .prototype .initialize .call (this);

      this ._mode      .addInterest ("set_mode__",      this);
      this ._parameter .addInterest ("set_parameter__", this);

      this .set_mode__ ();
      this .set_parameter__ ();
   },
   set_mode__: (() =>
   {
      const modes = new Map ([
         ["SPHERE",                      TextureCoordinateGeneratorModeType .SPHERE],
         ["CAMERASPACENORMAL",           TextureCoordinateGeneratorModeType .CAMERASPACENORMAL],
         ["CAMERASPACEPOSITION",         TextureCoordinateGeneratorModeType .CAMERASPACEPOSITION],
         ["CAMERASPACEREFLECTIONVECTOR", TextureCoordinateGeneratorModeType .CAMERASPACEREFLECTIONVECTOR],
         ["SPHERE-LOCAL",                TextureCoordinateGeneratorModeType .SPHERE_LOCAL],
         ["COORD",                       TextureCoordinateGeneratorModeType .COORD],
         ["COORD-EYE",                   TextureCoordinateGeneratorModeType .COORD_EYE],
         ["NOISE",                       TextureCoordinateGeneratorModeType .NOISE],
         ["NOISE-EYE",                   TextureCoordinateGeneratorModeType .NOISE_EYE],
         ["SPHERE-REFLECT",              TextureCoordinateGeneratorModeType .SPHERE_REFLECT],
         ["SPHERE-REFLECT-LOCAL",        TextureCoordinateGeneratorModeType .SPHERE_REFLECT_LOCAL],
      ]);

      return function ()
      {
         this .mode = modes .get (this ._mode .getValue ());

         if (this .mode === undefined)
            this .mode = TextureCoordinateGeneratorModeType .SPHERE;
      };
   })(),
   set_parameter__ ()
   {
      const length = Math .min (this .parameter .length, this ._parameter .length)

      for (let i = 0; i < length; ++ i)
         this .parameter [i] = this ._parameter [i];

      this .parameter .fill (0, length);
   },
   addPointToChannel (index, array)
   {
      array .push (0, 0, 0, 1);
   },
   addPoints (array)
   {
      return array;
   },
   setShaderUniforms (gl, shaderObject, channel = 0)
   {
      const uniforms = shaderObject .x3d_TextureCoordinateGenerator [channel];

      gl .uniform1i  (uniforms .mode,      this .mode);
      gl .uniform1fv (uniforms .parameter, this .parameter);
   },
});

Object .defineProperties (TextureCoordinateGenerator,
{
   ... Core_X3DNode .getStaticProperties ("TextureCoordinateGenerator", "Texturing", 2, "texCoord", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",  new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mapping",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mode",      new x_ite_Fields .SFString ("SPHERE")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "parameter", new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const TextureCoordinateGenerator_default_ = TextureCoordinateGenerator;
;

/* harmony default export */ const Texturing_TextureCoordinateGenerator = (Namespace/* default */.A .add ("TextureCoordinateGenerator", TextureCoordinateGenerator_default_));
;// ./src/x_ite/Components/Grouping/StaticGroup.js































const CLONE_COUNT = 2; // Minimum number of shapes that must be cloned to become an InstancedShape.

// No support for X3DBindableNode nodes, local lights. X3DLocalFog, local ClipPlane nodes, LOD, Billboard node.

function StaticGroup (executionContext)
{
   Core_X3DChildNode     .call (this, executionContext);
   Grouping_X3DBoundedObject .call (this, executionContext);

   this .addType (Base_X3DConstants .StaticGroup);

   this .groupNode  = new Grouping_Group (this .getExecutionContext ());
   this .bbox       = new Geometry_Box3 ();
   this .shadowBBox = new Geometry_Box3 ();
}

Object .assign (Object .setPrototypeOf (StaticGroup .prototype, Core_X3DChildNode .prototype),
   Grouping_X3DBoundedObject .prototype,
{
   initialize ()
   {
      Core_X3DChildNode     .prototype .initialize .call (this);
      Grouping_X3DBoundedObject .prototype .initialize .call (this);

      this ._children .addFieldInterest (this .groupNode ._children);

      this .groupNode ._children = this ._children;
      this .groupNode .setPrivate (true);
      this .groupNode .setup ();

      // Connect after setup for correct order of events.
      this .groupNode ._children .addInterest ("set_children__", this);

      this .set_children__ ();
   },
   getBBox (bbox, shadows)
   {
      if (this .isDefaultBBoxSize ())
      {
         if (this .optimizedGroup)
            return bbox .assign (shadows ? this .shadowBBox : this .bbox);

         return this .groupNode .getBBox (bbox, shadows);
      }

      return bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
   },
   getShapes (shapes, modelMatrix)
   {
      return this .groupNode .getShapes (shapes, modelMatrix);
   },
   set_children__ ()
   {
      if (this .optimizedGroup)
         this .disconnectChildNode (this .optimizedGroup);

      this .connectChildNode (this .groupNode, [Rendering_TraverseType .CAMERA]);

      this .optimizedGroup = null;
   },
   traverse (type, renderObject)
   {
      if (!this .optimizedGroup)
         this .createStaticShapes ();

      this .optimizedGroup .traverse (type, renderObject);
   },
   async createStaticShapes ()
   {
      // Temporarily assign Group node.

      this .optimizedGroup = this .groupNode;

      // Check if scene is currently loading something.

      const scene = this .getScene ();

      if (scene ._loadCount .getValue ())
      {
         scene ._loadCount .addFieldCallback (this, () =>
         {
            if (scene ._loadCount .getValue ())
               return;

            scene ._loadCount .removeFieldCallback (this);

            this .set_children__ ();
         });
      }
      else
      {
         // Create static shapes.

         this .optimizedGroup = await this .optimizeGroups (this .createGroups ());

         this .disconnectChildNode (this .groupNode);
         this .connectChildNode (this .optimizedGroup, [Rendering_TraverseType .CAMERA]);

         this .optimizedGroup .getBBox (this .bbox);
         this .optimizedGroup .getBBox (this .shadowBBox, true);
      }
   },
   createGroups: (() =>
   {
      const style = { style: "CLEAN", names: false };

      return function ()
      {
         // Traverse Group node to get render contexts.

         const browser = this .getBrowser ();

         if (browser .getBrowserOption ("Debug"))
            console .info (`Rebuilding StaticGroup "${this .getName () || "unnamed"}".`);

         const shapes = this .groupNode .getShapes ([ ], Numbers_Matrix4 .IDENTITY);

         // Determine groups that can be combined.
         // Sort out ParticleSystem nodes.
         // Sort out TextureCoordinateGenerator nodes.

         const
            clonesIndex  = new Map (shapes .map (({shapeNode}) => [shapeNode, [ ]])),
            groupsIndex  = { },
            singlesIndex = { };

         for (const context of shapes)
         {
            const
               shapeNode      = context .shapeNode,
               appearanceNode = shapeNode .getAppearance (),
               geometryNode   = shapeNode .getGeometry ();

            if (shapeNode .getType () .includes (Base_X3DConstants .ParticleSystem))
            {
               if (shapeNode ._geometryType .getValue () === "GEOMETRY")
               {
                  if (!geometryNode ?.getVertices () .length)
                     continue;
               }

               if (!shapeNode ._isActive .getValue ())
                  continue;

               if (!shapeNode ._createParticles .getValue ())
                  continue;
            }
            else if (!geometryNode ?.getVertices () .length)
            {
               continue;
            }

            // Sort out ParticleSystem and InstancedShape nodes.
            if (shapeNode .getShapeKey () > 0 || this .hasTextureCoordinateGenerator (geometryNode))
            {
               const group = singlesIndex [context .modelMatrix] ??= [ ];

               group .push (context);
               continue;
            }

            let key = "";

            key += geometryNode .getGeometryType ();
            key += geometryNode .isSolid () ? 1 : 0;
            key += geometryNode .getFogDepths () .length ? 1 : 0;
            key += shapeNode .isTransparent () ? 1 : 0;
            key += shapeNode ._pointerEvents .getValue () ? 1 : 0;
            key += shapeNode ._castShadow .getValue () ? 1 : 0;
            key += shapeNode ._bboxDisplay .getValue () ? 1 : 0;
            key += ".";
            key += appearanceNode .toVRMLString (style);

            const
               clones = clonesIndex .get (shapeNode),
               group  = groupsIndex [key] ??= [ ];

            clones .push (context);
            group  .push (context);
         }

         // Sort out shapes that are not cloned.

         for (const [id, group] of clonesIndex .entries ())
         {
            if (group .length < CLONE_COUNT)
               clonesIndex .delete (id);
         }

         // Create arrays.

         const
            clonesGroups  = Array .from (clonesIndex .values ()),
            combineGroups = Object .values (groupsIndex)
               .map (group => group .filter (({shapeNode}) => !clonesIndex .has (shapeNode)))
               .filter (group => group .length),
            singlesGroups = Object .values (singlesIndex);

         if (browser .getBrowserOption ("Debug"))
         {
            console .info (`StaticGroup will create ${clonesGroups .length + combineGroups .length + singlesGroups .length} static nodes from the previous ${shapes .length} nodes.`);
         }

         return { clonesGroups, combineGroups, singlesGroups };
      };
   })(),
   hasTextureCoordinateGenerator (geometryNode)
   {
      const texCoord = geometryNode ._texCoord ?.getValue ();

      if (texCoord instanceof Texturing_TextureCoordinateGenerator)
         return true;

      if (texCoord instanceof Texturing_MultiTextureCoordinate)
      {
         if (texCoord ._texCoord .some (tc => tc .getValue () instanceof Texturing_TextureCoordinateGenerator))
            return true;
      }

      return false;
   },
   async optimizeGroups ({ clonesGroups, combineGroups, singlesGroups })
   {
      if (clonesGroups .length)
         await this .getBrowser () .loadComponents ("X_ITE");

      // Create static shapes.

      if (DEVELOPMENT)
         console .time ("StaticGroup");

      const visibleNodes = [ ];

      clonesGroups  .forEach (group => this .combineClones (group, visibleNodes));
      combineGroups .forEach (group => this .combineShapes (group, visibleNodes));
      singlesGroups .forEach (group => this .normalizeSingleShapes (group, visibleNodes));

      // Create group of all optimized shapes.

      const optimizedGroup = new Grouping_Group (this .getExecutionContext ());

      optimizedGroup ._children = visibleNodes;

      optimizedGroup .setPrivate (true);
      optimizedGroup .setup ();

      if (DEVELOPMENT)
         console .timeEnd ("StaticGroup");

      return optimizedGroup;
   },
   combineClones: (() =>
   {
      const
         t  = new Numbers_Vector3 (),
         r  = new Numbers_Rotation4 (),
         s  = new Numbers_Vector3 (),
         so = new Numbers_Rotation4 ();

      return function (group, visibleNodes)
      {
         const
            browser          = this .getBrowser (),
            executionContext = this .getExecutionContext (),
            InstancedShape   = browser .getConcreteNodes () .get ("InstancedShape"),
            instancedShape   = new InstancedShape (executionContext),
            shapeNode0       = group [0] .shapeNode;

         for (const { modelMatrix } of group)
         {
            modelMatrix .getTransform (t, r, s, so);

            instancedShape ._translations      .push (t);
            instancedShape ._rotations         .push (r);
            instancedShape ._scales            .push (s);
            instancedShape ._scaleOrientations .push (so);
         }

         instancedShape ._pointerEvents = shapeNode0 ._pointerEvents;
         instancedShape ._castShadow    = shapeNode0 ._castShadow;
         instancedShape ._bboxDisplay   = shapeNode0 ._bboxDisplay;
         instancedShape ._appearance    = shapeNode0 ._appearance;
         instancedShape ._geometry      = shapeNode0 ._geometry;

         instancedShape .setPrivate (true);
         instancedShape .setup ();

         visibleNodes .push (instancedShape);
      };
   })(),
   combineShapes (group, visibleNodes)
   {
      const
         executionContext = this .getExecutionContext (),
         shapeNode0       = group [0] .shapeNode,
         newShapeNode     = shapeNode0 .copy (executionContext);

      let
         newGeometryNode = null,
         numPoints       = 0;

      for (const { modelMatrix, shapeNode } of group)
      {
         const normalizedGeometry = this .normalizeGeometry (modelMatrix, shapeNode);

         if (!newGeometryNode)
         {
            newGeometryNode = normalizedGeometry;
            numPoints       = normalizedGeometry ._coord .point .length;
            continue;
         }

         // vertexCount

         if (newGeometryNode .getGeometryType () === 1)
         {
            const vertexCount = newGeometryNode ._vertexCount;

            vertexCount .assign (vertexCount .concat (normalizedGeometry ._vertexCount));
         }

         // Attribute Nodes

         const attrib = newGeometryNode ._attrib;

         for (const node of normalizedGeometry ._attrib)
         {
            const
               normalizedAttrib = node .getValue (),
               name            = normalizedAttrib ._name .getValue ();

            let newAttribNode = attrib .find (a => a .name === name) ?.getValue ();

            if (!newAttribNode)
            {
               newAttribNode = normalizedAttrib .create (executionContext);

               attrib .push (newAttribNode);
            }

            newAttribNode ._name          = normalizedAttrib ._name;
            newAttribNode ._numComponents = normalizedAttrib ._numComponents;

            const
               value         = newAttribNode ._value,
               numComponents = normalizedAttrib ._numComponents ?.getValue () ?? 1;

            if (value .length < numPoints * numComponents)
               value .resize (numPoints * numComponents);

            value .assign (value .concat (normalizedAttrib ._value));
         }

         // FogCoordinate

         const normalizedFogCoord = normalizedGeometry ._fogCoord .getValue ();

         if (normalizedFogCoord ?._depth .length)
         {
            if (!newGeometryNode ._fogCoord .getValue ())
               newGeometryNode ._fogCoord = new EnvironmentalEffects_FogCoordinate (executionContext);

            const depth = newGeometryNode ._fogCoord .depth;

            if (depth .length < numPoints)
               depth .resize (numPoints);

            depth .assign (depth .concat (normalizedFogCoord ._depth));
         }

         // Color

         const normalizedColor = normalizedGeometry ._color .getValue ();

         if (normalizedColor ?._color .length)
         {
            if (!newGeometryNode ._color .getValue ())
               newGeometryNode ._color = normalizedColor .create (executionContext);

            const color = newGeometryNode ._color .color;

            if (color .length < numPoints)
               color .resize (numPoints, Numbers_Color4 .WHITE);

            color .assign (color .concat (normalizedColor ._color));
         }

         // TextureCoordinate

         if (newGeometryNode ._texCoord)
         {
            const normalizedTexCoords = normalizedGeometry ._texCoord .getValue ();

            if (!newGeometryNode ._texCoord .getValue ())
               newGeometryNode ._texCoord = new Texturing_MultiTextureCoordinate (executionContext);

            const texCoords = newGeometryNode ._texCoord .texCoord;

            for (const node of normalizedTexCoords ._texCoord)
            {
               const
                  normalizedTexCoord = node .getValue (),
                  mapping            = normalizedTexCoord ._mapping .getValue ();

               let newTexCoordNode = texCoords .find (tc => tc .mapping === mapping) ?.getValue ();

               if (!newTexCoordNode)
               {
                  newTexCoordNode = normalizedTexCoord .create (executionContext);

                  texCoords .push (newTexCoordNode);
               }

               newTexCoordNode ._mapping = normalizedTexCoord ._mapping;

               const point = newTexCoordNode ._point;

               if (point .length < numPoints)
                  point .resize (numPoints);

               point .assign (point .concat (normalizedTexCoord ._point));
            }
         }

         // Tangent

         const normalizedTangent = normalizedGeometry ._tangent .getValue ();

         if (normalizedTangent ?._vector .length)
         {
            if (!newGeometryNode ._tangent .getValue ())
               newGeometryNode ._tangent = new Rendering_Tangent (executionContext);

            const vector = newGeometryNode ._tangent .vector;

            if (vector .length < numPoints)
               vector .resize (numPoints);

            vector .assign (vector .concat (normalizedTangent ._vector));
         }

         // Normal

         const normalizedNormal = normalizedGeometry ._normal .getValue ();

         if (normalizedNormal ?._vector .length)
         {
            if (!newGeometryNode ._normal .getValue ())
               newGeometryNode ._normal = new Rendering_Normal (executionContext);

            const vector = newGeometryNode ._normal .vector;

            if (vector .length < numPoints)
               vector .resize (numPoints);

            vector .assign (vector .concat (normalizedNormal ._vector));
         }

         // Coordinate

         const normalizedCoord = normalizedGeometry ._coord .getValue ();

         if (normalizedCoord ?._point .length)
         {
            if (!newGeometryNode ._coord .getValue ())
               newGeometryNode ._coord = new Rendering_Coordinate (executionContext);

            const point = newGeometryNode ._coord .point;

            point .assign (point .concat (normalizedCoord ._point));

            numPoints = point .length;
         }
      }

      // Setup X3DGeometryNode node.

      newGeometryNode ._attrib    .forEach (a => a .getValue () .setup ());
      newGeometryNode ._fogCoord  .getValue () ?.setup ();
      newGeometryNode ._color     .getValue () ?.setup ();
      newGeometryNode ._texCoord ?.texCoord .forEach (tc => tc .getValue () .setup ());
      newGeometryNode ._texCoord ?.getValue () ?.setup ();
      newGeometryNode ._tangent   .getValue () ?.setup ();
      newGeometryNode ._normal    .getValue () ?.setup ();
      newGeometryNode ._coord     .getValue () ?.setup ();

      newGeometryNode .setup ();

      // Setup Shape node.

      newShapeNode ._geometry = newGeometryNode;

      newShapeNode .setPrivate (true);
      newShapeNode .setup ();

      visibleNodes .push (newShapeNode);
   },
   normalizeGeometry: (() =>
   {
      const GeometryTypes = [
         Rendering_PointSet,
         Rendering_LineSet,
         Rendering_TriangleSet,
         Rendering_TriangleSet,
      ];

      const FieldTypes = [
         x_ite_Fields .MFFloat,
         x_ite_Fields .MFVec2f,
         x_ite_Fields .MFVec3f,
         x_ite_Fields .MFVec4f,
      ];

      const
         tangent = new Numbers_Vector4 (),
         normal  = new Numbers_Vector3 (),
         vertex  = new Numbers_Vector4 (),
         point   = new Numbers_Vector3 ();

      return function (modelMatrix, shapeNode)
      {
         const
            browser          = this .getBrowser (),
            executionContext = this .getExecutionContext (),
            geometryNode     = shapeNode .getGeometry (),
            GeometryType     = GeometryTypes [geometryNode .getGeometryType ()],
            newGeometryNode  = new GeometryType (executionContext);

         // Attribute Nodes

         const
            attribs     = geometryNode .getAttribs (),
            attribNodes = geometryNode .getAttrib ();

         const newAttribNodes = attribs .map ((attrib, i) =>
         {
            const
               attribNode    = attribNodes [i],
               newAttribNode = attribNode .create (executionContext);

            newAttribNode ._name          = attribNode. _name;
            newAttribNode ._numComponents = attribNode ._numComponents;
            newAttribNode ._value         = attrib .getValue ();

            return newAttribNode;
         });

         newGeometryNode ._attrib = newAttribNodes;

         // FogCoordinate

         const fogDepthArray = geometryNode .getFogDepths () .getValue ();

         if (fogDepthArray .length)
         {
            const newFogCoordNode = new EnvironmentalEffects_FogCoordinate (executionContext);

            newFogCoordNode ._depth    = fogDepthArray;
            newGeometryNode ._fogCoord = newFogCoordNode;
         }

         // Color

         const colorArray = geometryNode .getColors () .getValue ();

         let newColor;

         if (colorArray .length)
         {
            if (shapeNode .isTransparent ())
            {
               newColor = new Rendering_ColorRGBA (executionContext);

               newColor ._color = colorArray;
            }
            else
            {
               newColor = new Rendering_Color (executionContext);

               newColor ._color = colorArray .filter ((c, i) => i % 4 < 3);
            }

            newGeometryNode ._color = newColor;
         }

         // TextureCoordinate

         if (newGeometryNode ._texCoord)
         {
            const
               textureCoordinateNode = geometryNode .getTextureCoordinate (),
               multiTexCoords        = geometryNode .getMultiTexCoords ();

            const newTexCoordNodes = multiTexCoords .map ((texCoords, i) =>
            {
               const texCoordNode = textureCoordinateNode instanceof Texturing_MultiTextureCoordinate
                  ? textureCoordinateNode .getTextureCoordinates () [i]
                     ?? this .getBrowser () .getDefaultTextureCoordinate ()
                  : textureCoordinateNode;

               if (texCoordNode instanceof Texturing_TextureCoordinateGenerator)
                  return texCoordNode .copy (executionContext);

               const
                  texCoordArray       = texCoords .getValue (),
                  TextureCoordinate4D = browser .getConcreteNodes () .get ("TextureCoordinate4D"),
                  newTexCoordNode     = new (TextureCoordinate4D ?? Texturing_TextureCoordinate) (executionContext);

               newTexCoordNode ._mapping = texCoordNode ._mapping;

               if (TextureCoordinate4D)
                  newTexCoordNode ._point = texCoordArray;
               else
                  newTexCoordNode ._point = texCoordArray .filter ((p, i) => i % 4 < 2);

               return newTexCoordNode;
            });

            const newTexCoordNode = new Texturing_MultiTextureCoordinate (executionContext);

            newTexCoordNode ._texCoord = newTexCoordNodes;
            newGeometryNode ._texCoord = newTexCoordNode;
         }

         // Tangents

         const
            normalMatrix = modelMatrix .submatrix .inverse () .transpose (),
            tangentArray = geometryNode .getTangents ();

         if (tangentArray .length)
         {
            const
               numTangents    = tangentArray .length,
               newTangentNode = new Rendering_Tangent (executionContext),
               newVectors     = new Float32Array (numTangents);

            for (let i = 0; i < numTangents; i += 4)
            {
               normal .set (tangentArray [i], tangentArray [i + 1], tangentArray [i + 2]);
               normalMatrix .multVecMatrix (normal);
               newVectors .set (tangent .set (normal .x, normal .y, normal .z, tangentArray [i + 3]), i);
            }

            newTangentNode  ._vector  = newVectors;
            newGeometryNode ._tangent = newTangentNode;
         }

         // Normals

         const normalArray = geometryNode .getNormals ();

         if (normalArray .length)
         {
            const
               numNormals    = normalArray .length,
               newNormalNode = new Rendering_Normal (executionContext),
               newVectors    = new Float32Array (numNormals);

            for (let i = 0; i < numNormals; i += 3)
            {
               normal .set (normalArray [i], normalArray [i + 1], normalArray [i + 2]);
               normalMatrix .multVecMatrix (normal);
               newVectors .set (normal, i);
            }

            newNormalNode   ._vector = newVectors;
            newGeometryNode ._normal = newNormalNode;
         }

         // Coordinate

         const
            vertexArray  = geometryNode .getVertices (),
            numVertices  = vertexArray .length,
            newCoordNode = new Rendering_Coordinate (executionContext),
            newPoints    = new Float32Array (numVertices / 4 * 3);

         for (let i = 0, o = 0; i < numVertices; i += 4, o += 3)
         {
            vertex .set (vertexArray [i], vertexArray [i + 1], vertexArray [i + 2], vertexArray [i + 3]);
            modelMatrix .multVecMatrix (vertex);
            newPoints .set (point .assign (vertex) .divide (vertex .w), o);
         }

         newCoordNode    ._point = newPoints;
         newGeometryNode ._coord = newCoordNode;

         // Common fields

         switch (geometryNode .getGeometryType ())
         {
            case 1:
            {
               newGeometryNode ._vertexCount = new Array (numVertices / 8) .fill (2);
               break;
            }
            case 2:
            case 3:
            {
               newGeometryNode .setGeometryType (geometryNode .getGeometryType ());
               newGeometryNode ._solid = geometryNode .isSolid ();

               if (!(geometryNode ._ccw ?.getValue () ?? true))
               {
                  for (const attrib of newGeometryNode ._attrib)
                  {
                     const numComponents = Math_Algorithm .clamp (attrib .numComponents, 1, 4);

                     if (numComponents === 1)
                     {
                        attrib .value .reverse ();
                     }
                     else
                     {
                        const value = new (FieldTypes [numComponents - 1]) ();

                        value .setValue (attrib .value .shrinkToFit ());
                        value .reverse ();

                        attrib .value = value .shrinkToFit ();
                     }
                  }

                  newGeometryNode ._fogCoord .getValue () ?._depth    .reverse ();
                  newGeometryNode ._color    .getValue () ?._color    .reverse ();
                  newGeometryNode ._texCoord .getValue () ?._texCoord .forEach (tc => tc .point .reverse ());
                  newGeometryNode ._tangent  .getValue () ?._vector   .reverse ();
                  newGeometryNode ._normal   .getValue () ?._vector   .reverse ();
                  newGeometryNode ._coord    .getValue () ?._point    .reverse ();
               }

               break;
            }
         }

         return newGeometryNode;
      };
   })(),
   normalizeSingleShapes: (() =>
   {
      const
         t  = new Numbers_Vector3 (),
         r  = new Numbers_Rotation4 (),
         s  = new Numbers_Vector3 (),
         so = new Numbers_Rotation4 ();

      return function (group, visibleNodes)
      {
         const
            executionContext = this .getExecutionContext (),
            newTransformNode = new Grouping_Transform (executionContext),
            modelMatrix      = group [0] .modelMatrix;

         modelMatrix .getTransform (t, r, s, so);

         newTransformNode ._translation      = t;
         newTransformNode ._rotation         = r;
         newTransformNode ._scale            = s;
         newTransformNode ._scaleOrientation = so;

         for (const { shapeNode } of group)
            newTransformNode ._children .push (shapeNode);

         newTransformNode .setPrivate (true);
         newTransformNode .setup ();

         visibleNodes .push (newTransformNode);
      };
   })(),
   dispose ()
   {
      Grouping_X3DBoundedObject .prototype .dispose .call (this);
      Core_X3DChildNode     .prototype .dispose .call (this);
   },
});

Object .defineProperties (StaticGroup,
{
   ... Core_X3DNode .getStaticProperties ("StaticGroup", "Grouping", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",    new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",  new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "children",    new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const StaticGroup_default_ = StaticGroup;
;

/* harmony default export */ const Grouping_StaticGroup = (Namespace/* default */.A .add ("StaticGroup", StaticGroup_default_));
;// ./src/x_ite/Components/Grouping/Switch.js







function Switch (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Switch);

   // Legacy

   if (executionContext .getSpecificationVersion () == 2.0)
      this .addAlias ("choice", this ._children);
}

Object .assign (Object .setPrototypeOf (Switch .prototype, Grouping_X3DGroupingNode .prototype),
{
   initialize ()
   {
      Grouping_X3DGroupingNode .prototype .initialize .call (this);

      this ._whichChoice .addInterest ("requestRebuild", this);
   },
   addChildren ()
   { },
   removeChildren ()
   { },
   set_addChildren__ ()
   {
      Grouping_X3DGroupingNode .prototype .set_addChildren__ .call (this);

      this .set_children__ ();
   },
   set_removeChildren__ ()
   {
      Grouping_X3DGroupingNode .prototype .set_removeChildren__ .call (this);

      this .set_children__ ();
   },
   set_children__ ()
   {
      this .clearChildren ();

      // Add single child.

      const whichChoice = this ._whichChoice .getValue ();

      if (whichChoice >= 0 && whichChoice < this ._children .length)
         this .addChild (this ._children [whichChoice]);

      this .set_objects__ ();
   },
});

Object .defineProperties (Switch,
{
   ... Core_X3DNode .getStaticProperties ("Switch", "Grouping", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "whichChoice",    new x_ite_Fields .SFInt32 (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",       new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Switch_default_ = Switch;
;

/* harmony default export */ const Grouping_Switch = (Namespace/* default */.A .add ("Switch", Switch_default_));
;// ./src/x_ite/Components/GroupingComponent.js









const GroupingComponent_default_ = {
   name: "Grouping",
   concreteNodes:
   [
      Grouping_Group,
      Grouping_StaticGroup,
      Grouping_Switch,
      Grouping_Transform,
   ],
   abstractNodes:
   [
      Grouping_X3DBoundedObject,
      Grouping_X3DGroupingNode,
      Grouping_X3DTransformMatrix3DNode,
      Grouping_X3DTransformNode,
   ],
};
;

/* harmony default export */ const GroupingComponent = (Namespace/* default */.A .add ("GroupingComponent", GroupingComponent_default_));
;// ./src/x_ite/Components/Interpolation/ColorInterpolator.js








function ColorInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ColorInterpolator);

   this .hsv = [ ];
}

Object .assign (Object .setPrototypeOf (ColorInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const keyValue = this ._keyValue;

      if (keyValue .length < this ._key .length)
         this ._keyValue .resize (this ._key .length, keyValue .length ? keyValue [this ._keyValue .length - 1] : new x_ite_Fields .SFColor ());

      this .hsv .length = 0;

      for (const value of keyValue)
         this .hsv .push (value .getHSV ());

      // If there already was an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate: (() =>
   {
      const value = [ ];

      return function (index0, index1, weight)
      {
         Numbers_Color3 .lerp (this .hsv [index0], this .hsv [index1], weight, value);

         this ._value_changed .setHSV (value [0], value [1], value [2]);
      };
   })(),
});

Object .defineProperties (ColorInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("ColorInterpolator", "Interpolation", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFColor ()),
      ]),
      enumerable: true,
   },
});

const ColorInterpolator_default_ = ColorInterpolator;
;

/* harmony default export */ const Interpolation_ColorInterpolator = (Namespace/* default */.A .add ("ColorInterpolator", ColorInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/CoordinateInterpolator.js








function CoordinateInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .CoordinateInterpolator);
}

Object .assign (Object .setPrototypeOf (CoordinateInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   set_keyValue__ ()
   {
      // If there already was an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      const keyValue = this ._keyValue .getValue ();

      let size = this ._key .length ? Math .floor (this ._keyValue .length / this ._key .length) : 0;

      this ._value_changed .length = size;

      const value_changed = this ._value_changed .getValue ();

      index0 *= size;
      index1  = index0 + (this ._key .length > 1 ? size : 0);

      index0 *= 3;
      index1 *= 3;
      size   *= 3;

      for (let i0 = 0; i0 < size; i0 += 3)
      {
         const
            i1 = i0 + 1,
            i2 = i0 + 2;

         value_changed [i0] = Math_Algorithm .lerp (keyValue [index0 + i0], keyValue [index1 + i0], weight);
         value_changed [i1] = Math_Algorithm .lerp (keyValue [index0 + i1], keyValue [index1 + i1], weight);
         value_changed [i2] = Math_Algorithm .lerp (keyValue [index0 + i2], keyValue [index1 + i2], weight);
      }

      this ._value_changed .addEvent ();
   },
});

Object .defineProperties (CoordinateInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("CoordinateInterpolator", "Interpolation", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const CoordinateInterpolator_default_ = CoordinateInterpolator;
;

/* harmony default export */ const Interpolation_CoordinateInterpolator = (Namespace/* default */.A .add ("CoordinateInterpolator", CoordinateInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/CoordinateInterpolator2D.js








function CoordinateInterpolator2D (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .CoordinateInterpolator2D);
}

Object .assign (Object .setPrototypeOf (CoordinateInterpolator2D .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   set_keyValue__ ()
   {
      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      const keyValue = this ._keyValue .getValue ();

      let size = this ._key .length ? Math .floor (this ._keyValue .length / this ._key .length) : 0;

      this ._value_changed .length = size;

      const value_changed = this ._value_changed .getValue ();

      index0 *= size;
      index1  = index0 + (this ._key .length > 1 ? size : 0);

      index0 *= 2;
      index1 *= 2;
      size   *= 2;

      for (let i0 = 0; i0 < size; i0 += 2)
      {
         const i1 = i0 + 1;

         value_changed [i0] = Math_Algorithm .lerp (keyValue [index0 + i0], keyValue [index1 + i0], weight);
         value_changed [i1] = Math_Algorithm .lerp (keyValue [index0 + i1], keyValue [index1 + i1], weight);
      }

      this ._value_changed .addEvent ();
   },
});

Object .defineProperties (CoordinateInterpolator2D,
{
   ... Core_X3DNode .getStaticProperties ("CoordinateInterpolator2D", "Interpolation", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .MFVec2f ()),
      ]),
      enumerable: true,
   },
});

const CoordinateInterpolator2D_default_ = CoordinateInterpolator2D;
;

/* harmony default export */ const Interpolation_CoordinateInterpolator2D = (Namespace/* default */.A .add ("CoordinateInterpolator2D", CoordinateInterpolator2D_default_));
;// ./src/x_ite/Components/Interpolation/NormalInterpolator.js









function NormalInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .NormalInterpolator);
}

Object .assign (Object .setPrototypeOf (NormalInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate: (() =>
   {
      const
         keyValue0 = new Numbers_Vector3 (),
         keyValue1 = new Numbers_Vector3 ();

      return function (index0, index1, weight)
      {
         const keyValue = this ._keyValue .getValue ();

         let size = this ._key .length ? Math .floor (this ._keyValue .length / this ._key .length) : 0;

         this ._value_changed .length = size;

         const value_changed = this ._value_changed .getValue ();

         index0 *= size;
         index1  = index0 + (this ._key .length > 1 ? size : 0);

         index0 *= 3;
         index1 *= 3;
         size   *= 3;

         for (let i0 = 0; i0 < size; i0 += 3)
         {
            const
               i1 = i0 + 1,
               i2 = i0 + 2;

            keyValue0 .set (keyValue [index0 + i0], keyValue [index0 + i1], keyValue [index0 + i2]);
            keyValue1 .set (keyValue [index1 + i0], keyValue [index1 + i1], keyValue [index1 + i2]);

            const value = Math_Algorithm .simpleSlerp (keyValue0, keyValue1, weight);

            value_changed [i0] = value [0];
            value_changed [i1] = value [1];
            value_changed [i2] = value [2];
         }

         this ._value_changed .addEvent ();
      };
   })(),
});

Object .defineProperties (NormalInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("NormalInterpolator", "Interpolation", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .MFVec3f ()),
      ]),
      enumerable: true,
   },
});

const NormalInterpolator_default_ = NormalInterpolator;
;

/* harmony default export */ const Interpolation_NormalInterpolator = (Namespace/* default */.A .add ("NormalInterpolator", NormalInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/PositionInterpolator2D.js








function PositionInterpolator2D (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PositionInterpolator2D);
}

Object .assign (Object .setPrototypeOf (PositionInterpolator2D .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFVec2f ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate: (() =>
   {
      const keyValue = new Numbers_Vector2 ();

      return function (index0, index1, weight)
      {
         this ._value_changed = keyValue .assign (this ._keyValue [index0] .getValue ()) .lerp (this ._keyValue [index1] .getValue (), weight);
      };
   })(),
});

Object .defineProperties (PositionInterpolator2D,
{
   ... Core_X3DNode .getStaticProperties ("PositionInterpolator2D", "Interpolation", 3, "children", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",           new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",      new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed", new x_ite_Fields .SFVec2f ()),
      ]),
      enumerable: true,
   },
});

const PositionInterpolator2D_default_ = PositionInterpolator2D;
;

/* harmony default export */ const Interpolation_PositionInterpolator2D = (Namespace/* default */.A .add ("PositionInterpolator2D", PositionInterpolator2D_default_));
;// ./src/x_ite/Browser/Interpolation/CatmullRomSplineInterpolator.js



function CatmullRomSplineInterpolator ()
{
   this .T0 = [ ];
   this .T1 = [ ];
}

Object .assign (CatmullRomSplineInterpolator .prototype,
{
   generate: (() =>
   {
      const
         T  = [ ],
         Fp = [ ],
         Fm = [ ];

      return function (closed, key, keyValue, keyVelocity, normalizeVelocity)
      {
         const
            T0 = this .T0,
            T1 = this .T1;

         T0 .length = 0;
         T1 .length = 0;

         T  .length = 0;
         Fp .length = 0;
         Fm .length = 0;

         if (key .length > 1)
         {
            // T

            if (keyVelocity .length === 0)
            {
               if (closed)
                  T .push (this .divide (this .subtract (keyValue [1], keyValue [keyValue .length - 2]), 2));

               else
                  T .push (this .create ());

               for (let i = 1, length = keyValue .length - 1; i < length; ++ i)
                  T .push (this .divide (this .subtract (keyValue [i + 1], keyValue [i - 1]), 2));

               T .push (this .copy (T [0]));
            }
            else
            {
               for (let i = 0, length = keyVelocity .length; i < length; ++ i)
                  T .push (this .copy (keyVelocity [i]));

               if (normalizeVelocity)
               {
                  let Dtot = 0;

                  for (let i = 0, length = keyValue .length - 1; i < length; ++ i)
                     Dtot += this .distance (keyValue [i], keyValue [i + 1]);

                  for (let i = 0, length = T .length - 1; i < length; ++ i)
                  {
                     const Tia = this .magnitude (T [i]);

                     if (Tia)
                        T [i] = this .multiply (T [i], Dtot / Tia);
                  }
               }
            }

            // Fm, Fp

            if (closed)
            {
               const i_1 = key .length - 1;
               const i_2 = key .length - 2;

               const d = key [1] - key [0] + key [i_1] - key [i_2];

               Fm .push (2 * (key [1]   - key [0])   / d);
               Fp .push (2 * (key [i_1] - key [i_2]) / d);
            }
            else
            {
               Fm .push (1);
               Fp .push (1);
            }

            for (let i = 1, length = key .length - 1; i < length; ++ i)
            {
               const d = key [i + 1] - key [i - 1];

               Fm .push (2 * (key [i + 1] - key [i])     / d);
               Fp .push (2 * (key [i]     - key [i - 1]) / d);
            }

            Fm .push (Fm [0]);
            Fp .push (Fp [0]);

            // T0, T1

            for (let i = 0, length = T .length; i < length; ++ i)
            {
               T0 .push (this .multiply (T [i], Fp [i]));
               T1 .push (this .multiply (T [i], Fm [i]));
            }
         }
         else
         {
            T0 .push (this .create ());
            T1 .push (this .create ());
         }
      };
   })(),
   interpolate: (() =>
   {
      const S = new Numbers_Vector4 ();

      const H = new Numbers_Matrix4 ( 2, -2,  1,  1,
                             -3,  3, -2, -1,
                              0,  0,  1,  0,
                              1,  0,  0,  0);

      return function (index0, index1, weight, keyValue)
      {
         S .set (weight ** 3, weight ** 2, weight, 1);

         // Taking dot product from SH and C;

         return this .dot (H .multVecMatrix (S),
                           keyValue [index0],
                           keyValue [index1],
                           this .T0 [index0],
                           this .T1 [index1]);
      };
   })(),
});

const CatmullRomSplineInterpolator_default_ = CatmullRomSplineInterpolator;
;

/* harmony default export */ const Interpolation_CatmullRomSplineInterpolator = (Namespace/* default */.A .add ("CatmullRomSplineInterpolator", CatmullRomSplineInterpolator_default_));
;// ./src/x_ite/Browser/Interpolation/CatmullRomSplineInterpolatorTemplate.js


function CatmullRomSplineInterpolatorTemplate (Type)
{
   function CatmullRomSplineInterpolatorInstance ()
   {
      this .T0 = [ ];
      this .T1 = [ ];
   }

   Object .assign (Object .setPrototypeOf (CatmullRomSplineInterpolatorInstance .prototype, Interpolation_CatmullRomSplineInterpolator .prototype),
   {
      create ()
      {
         return new Type ();
      },
      copy (value)
      {
         return value .copy ();
      },
      subtract (lhs, rhs)
      {
         return lhs .copy () .subtract (rhs);
      },
      multiply (lhs, rhs)
      {
         return lhs .copy () .multiply (rhs);
      },
      divide (lhs, rhs)
      {
         return lhs .copy () .divide (rhs);
      },
      magnitude (value)
      {
         return value .norm ();
      },
      distance (lhs, rhs)
      {
         return lhs .distance (rhs);
      },
      dot: (() =>
      {
         const
            c0 = new Type (),
            c1 = new Type (),
            c2 = new Type (),
            c3 = new Type ();

         return function (SH, C0, C1, C2, C3)
         {
            c0 .assign (C0) .multiply (SH [0]);
            c1 .assign (C1) .multiply (SH [1]);
            c2 .assign (C2) .multiply (SH [2]);
            c3 .assign (C3) .multiply (SH [3]);

            return c0 .add (c1) .add (c2) .add (c3);
         };
      })(),
   });

   return CatmullRomSplineInterpolatorInstance;
}

const CatmullRomSplineInterpolatorTemplate_default_ = CatmullRomSplineInterpolatorTemplate;
;

/* harmony default export */ const Interpolation_CatmullRomSplineInterpolatorTemplate = (Namespace/* default */.A .add ("CatmullRomSplineInterpolatorTemplate", CatmullRomSplineInterpolatorTemplate_default_));
;// ./src/x_ite/Browser/Interpolation/CatmullRomSplineInterpolator3.js



const CatmullRomSplineInterpolator3 = Interpolation_CatmullRomSplineInterpolatorTemplate (Numbers_Vector3);

const CatmullRomSplineInterpolator3_default_ = CatmullRomSplineInterpolator3;
;

/* harmony default export */ const Interpolation_CatmullRomSplineInterpolator3 = (Namespace/* default */.A .add ("CatmullRomSplineInterpolator3", CatmullRomSplineInterpolator3_default_));
;// ./src/x_ite/Components/Interpolation/SplinePositionInterpolator.js








function SplinePositionInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SplinePositionInterpolator);

   this .spline = new Interpolation_CatmullRomSplineInterpolator3 ();
}

Object .assign (Object .setPrototypeOf (SplinePositionInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue          .addInterest ("set_keyValue__",          this);
      this ._keyVelocity       .addInterest ("set_keyVelocity__",       this);
      this ._normalizeVelocity .addInterest ("set_normalizeVelocity__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFVec3f ());

      this .set_keyVelocity__ ();
   },
   set_keyVelocity__ ()
   {
      if (this ._keyVelocity .length)
      {
         if (this ._keyVelocity .length < this ._key .length)
            this ._keyVelocity .resize (this ._key .length, new x_ite_Fields .SFVec3f ());
      }

      this .set_normalizeVelocity__ ();
   },
   set_normalizeVelocity__ ()
   {
      this .spline .generate (this ._closed .getValue (),
                              this ._key,
                              this ._keyValue,
                              this ._keyVelocity,
                              this ._normalizeVelocity .getValue ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      this ._value_changed = this .spline .interpolate (index0, index1, weight, this ._keyValue);
   },
});

Object .defineProperties (SplinePositionInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("SplinePositionInterpolator", "Interpolation", 4, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",      new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "closed",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",               new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",          new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyVelocity",       new x_ite_Fields .MFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalizeVelocity", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed",     new x_ite_Fields .SFVec3f ()),
      ]),
      enumerable: true,
   },
});

const SplinePositionInterpolator_default_ = SplinePositionInterpolator;
;

/* harmony default export */ const Interpolation_SplinePositionInterpolator = (Namespace/* default */.A .add ("SplinePositionInterpolator", SplinePositionInterpolator_default_));
;// ./src/x_ite/Browser/Interpolation/CatmullRomSplineInterpolator2.js



const CatmullRomSplineInterpolator2 = Interpolation_CatmullRomSplineInterpolatorTemplate (Numbers_Vector2);

const CatmullRomSplineInterpolator2_default_ = CatmullRomSplineInterpolator2;
;

/* harmony default export */ const Interpolation_CatmullRomSplineInterpolator2 = (Namespace/* default */.A .add ("CatmullRomSplineInterpolator2", CatmullRomSplineInterpolator2_default_));
;// ./src/x_ite/Components/Interpolation/SplinePositionInterpolator2D.js








function SplinePositionInterpolator2D (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SplinePositionInterpolator2D);

   this .spline = new Interpolation_CatmullRomSplineInterpolator2 ();
}

Object .assign (Object .setPrototypeOf (SplinePositionInterpolator2D .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue          .addInterest ("set_keyValue__",          this);
      this ._keyVelocity       .addInterest ("set_keyVelocity__",       this);
      this ._normalizeVelocity .addInterest ("set_normalizeVelocity__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFVec2f ());

      this .set_keyVelocity__ ();
   },
   set_keyVelocity__ ()
   {
      if (this ._keyVelocity .length)
      {
         if (this ._keyVelocity .length < this ._key .length)
            this ._keyVelocity .resize (this ._key .length, new x_ite_Fields .SFVec2f ());
      }

      this .set_normalizeVelocity__ ();
   },
   set_normalizeVelocity__ ()
   {
      this .spline .generate (this ._closed .getValue (),
                              this ._key,
                              this ._keyValue,
                              this ._keyVelocity,
                              this ._normalizeVelocity .getValue ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      this ._value_changed = this .spline .interpolate (index0, index1, weight, this ._keyValue);
   },
});

Object .defineProperties (SplinePositionInterpolator2D,
{
   ... Core_X3DNode .getStaticProperties ("SplinePositionInterpolator2D", "Interpolation", 4, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",      new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "closed",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",               new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",          new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyVelocity",       new x_ite_Fields .MFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalizeVelocity", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed",     new x_ite_Fields .SFVec2f ()),
      ]),
      enumerable: true,
   },
});

const SplinePositionInterpolator2D_default_ = SplinePositionInterpolator2D;
;

/* harmony default export */ const Interpolation_SplinePositionInterpolator2D = (Namespace/* default */.A .add ("SplinePositionInterpolator2D", SplinePositionInterpolator2D_default_));
;// ./src/x_ite/Browser/Interpolation/CatmullRomSplineInterpolator1.js


function CatmullRomSplineInterpolator1 ()
{
   Interpolation_CatmullRomSplineInterpolator .call (this);
}

Object .assign (Object .setPrototypeOf (CatmullRomSplineInterpolator1 .prototype, Interpolation_CatmullRomSplineInterpolator .prototype),
{
   create ()
   {
      return 0;
   },
   copy (value)
   {
      return value;
   },
   subtract (lhs, rhs)
   {
      return lhs - rhs;
   },
   multiply (lhs, rhs)
   {
      return lhs * rhs;
   },
   divide (lhs, rhs)
   {
      return lhs / rhs;
   },
   magnitude (value)
   {
      return Math .abs (value);
   },
   distance (lhs, rhs)
   {
      return Math .abs (lhs - rhs);
   },
   dot (SH, C0, C1, C2, C3)
   {
      return C0 * SH [0] + C1 * SH [1] + C2 * SH [2] + C3 * SH [3];
   },
});

const CatmullRomSplineInterpolator1_default_ = CatmullRomSplineInterpolator1;
;

/* harmony default export */ const Interpolation_CatmullRomSplineInterpolator1 = (Namespace/* default */.A .add ("CatmullRomSplineInterpolator1", CatmullRomSplineInterpolator1_default_));
;// ./src/x_ite/Components/Interpolation/SplineScalarInterpolator.js








function SplineScalarInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SplineScalarInterpolator);

   this .spline = new Interpolation_CatmullRomSplineInterpolator1 ();
}

Object .assign (Object .setPrototypeOf (SplineScalarInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue          .addInterest ("set_keyValue__",          this);
      this ._keyVelocity       .addInterest ("set_keyVelocity__",       this);
      this ._normalizeVelocity .addInterest ("set_normalizeVelocity__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFFloat ());

      this .set_keyVelocity__ ();
   },
   set_keyVelocity__ ()
   {
      if (this ._keyVelocity .length)
      {
         if (this ._keyVelocity .length < this ._key .length)
            this ._keyVelocity .resize (this ._key .length, new x_ite_Fields .SFFloat ());
      }

      this .set_normalizeVelocity__ ();
   },
   set_normalizeVelocity__ ()
   {
      this .spline .generate (this ._closed .getValue (),
                              this ._key,
                              this ._keyValue,
                              this ._keyVelocity,
                              this ._normalizeVelocity .getValue ());

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      this ._value_changed = this .spline .interpolate (index0, index1, weight, this ._keyValue);
   },
});

Object .defineProperties (SplineScalarInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("SplineScalarInterpolator", "Interpolation", 4, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",      new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "closed",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",               new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",          new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyVelocity",       new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalizeVelocity", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed",     new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const SplineScalarInterpolator_default_ = SplineScalarInterpolator;
;

/* harmony default export */ const Interpolation_SplineScalarInterpolator = (Namespace/* default */.A .add ("SplineScalarInterpolator", SplineScalarInterpolator_default_));
;// ./src/x_ite/Browser/Interpolation/SquadInterpolator.js


function SquadInterpolator ()
{
   this .s = [ ];
}

Object .assign (SquadInterpolator .prototype,
{
   generate (closed, key, keyValue)
   {
      const s = this .s;

      s .length = 0;

      if (key .length > 1)
      {
         if (closed)
         {
            s .push (Numbers_Rotation4 .spline (keyValue [key .length - 2] .getValue (),
                                        keyValue [0] .getValue (),
                                        keyValue [1] .getValue ()));
         }
         else
         {
            s .push (keyValue [0] .getValue ());
         }

         for (let i = 1, length = key .length - 1; i < length; ++ i)
         {
            s .push (Numbers_Rotation4 .spline (keyValue [i - 1] .getValue (),
                                        keyValue [i]     .getValue (),
                                        keyValue [i + 1] .getValue ()));
         }

         if (closed)
         {
            s .push (Numbers_Rotation4 .spline (keyValue [key .length - 2] .getValue (),
                                        keyValue [key .length - 1] .getValue (),
                                        keyValue [1] .getValue ()));
         }
         else
         {
            s .push (keyValue [key .length - 1] .getValue ());
         }
      }
      else if (key .length > 0)
      {
         s .push (keyValue [0] .getValue () .copy ());
      }
   },
   interpolate: (() =>
   {
      const result = new Numbers_Rotation4 ();

      return function (index0, index1, weight, keyValue)
      {
         const s = this .s;

         return result .assign (keyValue [index0] .getValue ()) .squad (s [index0],
                                                                        s [index1],
                                                                        keyValue [index1] .getValue (),
                                                                        weight);
      };
   })(),
});

const SquadInterpolator_default_ = SquadInterpolator;
;

/* harmony default export */ const Interpolation_SquadInterpolator = (Namespace/* default */.A .add ("SquadInterpolator", SquadInterpolator_default_));
;// ./src/x_ite/Components/Interpolation/SquadOrientationInterpolator.js








function SquadOrientationInterpolator (executionContext)
{
   Interpolation_X3DInterpolatorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SquadOrientationInterpolator);

   // Units

   this ._keyValue      .setUnit ("angle");
   this ._value_changed .setUnit ("angle");

   // Private properties

   this .squad = new Interpolation_SquadInterpolator ();
}

Object .assign (Object .setPrototypeOf (SquadOrientationInterpolator .prototype, Interpolation_X3DInterpolatorNode .prototype),
{
   initialize ()
   {
      Interpolation_X3DInterpolatorNode .prototype .initialize .call (this);

      this ._keyValue .addInterest ("set_keyValue__", this);
   },
   set_keyValue__ ()
   {
      const
         key      = this ._key,
         keyValue = this ._keyValue;

      if (keyValue .length < key .length)
         keyValue .resize (key .length, keyValue .length ? keyValue [keyValue .length - 1] : new x_ite_Fields .SFRotation ());

      this .squad .generate (this ._closed .getValue (),
                             this ._key,
                             this ._keyValue);

      // If there was already an set_fraction event in this frame, send a new value_changed to prevent glitches.
      if (this ._set_fraction .getModificationTime () >= this .getBrowser () .getCurrentTime ())
         this .set_fraction__ ();
   },
   interpolate (index0, index1, weight)
   {
      this ._value_changed = this .squad .interpolate (index0, index1, weight, this ._keyValue);
   },
});

Object .defineProperties (SquadOrientationInterpolator,
{
   ... Core_X3DNode .getStaticProperties ("SquadOrientationInterpolator", "Interpolation", 5, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,   "set_fraction",      new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "closed",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "key",               new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "keyValue",          new x_ite_Fields .MFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalizeVelocity", new x_ite_Fields .SFBool ()), // not supported
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "value_changed",     new x_ite_Fields .SFRotation ()),
      ]),
      enumerable: true,
   },
});

const SquadOrientationInterpolator_default_ = SquadOrientationInterpolator;
;

/* harmony default export */ const Interpolation_SquadOrientationInterpolator = (Namespace/* default */.A .add ("SquadOrientationInterpolator", SquadOrientationInterpolator_default_));
;// ./src/x_ite/Components/InterpolationComponent.js















const InterpolationComponent_default_ = {
   name: "Interpolation",
   concreteNodes:
   [
      Interpolation_ColorInterpolator,
      Interpolation_CoordinateInterpolator,
      Interpolation_CoordinateInterpolator2D,
      Interpolation_EaseInEaseOut,
      Interpolation_NormalInterpolator,
      Interpolation_OrientationInterpolator,
      Interpolation_PositionInterpolator,
      Interpolation_PositionInterpolator2D,
      Interpolation_ScalarInterpolator,
      Interpolation_SplinePositionInterpolator,
      Interpolation_SplinePositionInterpolator2D,
      Interpolation_SplineScalarInterpolator,
      Interpolation_SquadOrientationInterpolator,
   ],
   abstractNodes:
   [
      Interpolation_X3DInterpolatorNode,
   ],
};
;

/* harmony default export */ const InterpolationComponent = (Namespace/* default */.A .add ("InterpolationComponent", InterpolationComponent_default_));
;// ./src/x_ite/Components/LayeringComponent.js






const LayeringComponent_default_ = {
   name: "Layering",
   concreteNodes:
   [
      Layering_Layer,
      Layering_LayerSet,
      Layering_Viewport,
   ],
   abstractNodes:
   [
      Layering_X3DLayerNode,
      Layering_X3DViewportNode,
   ],
};
;

/* harmony default export */ const LayeringComponent = (Namespace/* default */.A .add ("LayeringComponent", LayeringComponent_default_));
;// ./src/x_ite/Components/Lighting/EnvironmentLight.js













/**
 * THIS NODE IS STILL EXPERIMENTAL.
 */

const EnvironmentLights = Utility_ObjectCache (EnvironmentLightContainer);

function EnvironmentLightContainer ()
{
   this .modelViewMatrix = new Utility_MatrixStack (Numbers_Matrix4);
   this .rotation        = new Numbers_Rotation4 ();
   this .rotationMatrix  = new Float32Array (9);
}

Object .assign (EnvironmentLightContainer .prototype,
{
   set (lightNode, groupNode, modelViewMatrix)
   {
      this .browser   = lightNode .getBrowser ();
      this .lightNode = lightNode;
      this .global    = lightNode .getGlobal ();

      this .modelViewMatrix .push (modelViewMatrix);
   },
   renderShadowMap (renderObject)
   { },
   setGlobalVariables (renderObject)
   {
      this .modelViewMatrix .get () .getTransform (null, this .rotation);

      this .rotation
         .multLeft (this .lightNode ._rotation .getValue ())
         .inverse ()
         .getMatrix (this .rotationMatrix);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         i        = shaderObject .numEnvironmentLights ++,
         uniforms = shaderObject .x3d_EnvironmentLight [i];

      if (!uniforms)
         return;

      const
         { browser, lightNode, global } = this,
         diffuseTexture  = lightNode .getDiffuseTexture (),
         specularTexture = lightNode .getSpecularTexture (),
         ggxLUTTexture   = browser .getLibraryTexture ("lut_ggx.png");

      const diffuseTextureUnit = global
         ? this .diffuseTextureUnit ??= browser .popGlobalTextureUnit ()
         : browser .popTextureUnit ();

      const specularTextureUnit = global
         ? this .specularTextureUnit ??= browser .popGlobalTextureUnit ()
         : browser .popTextureUnit ();

      const ggxLUTTextureUnit = global
         ? this .ggxLUTTextureUnit ??= browser .popGlobalTextureUnit ()
         : browser .popTextureUnit ();

      gl .activeTexture (gl .TEXTURE0 + diffuseTextureUnit);
      gl .bindTexture (gl .TEXTURE_CUBE_MAP, diffuseTexture ?.getTexture () ?? browser .getDefaultTextureCube ());
      gl .uniform1i (uniforms .diffuseTexture, diffuseTextureUnit);

      gl .activeTexture (gl .TEXTURE0 + specularTextureUnit);
      gl .bindTexture (gl .TEXTURE_CUBE_MAP, specularTexture ?.getTexture () ?? browser .getDefaultTextureCube ());
      gl .uniform1i (uniforms .specularTexture, specularTextureUnit);

      gl .activeTexture (gl .TEXTURE0 + ggxLUTTextureUnit);
      gl .bindTexture (gl .TEXTURE_2D, ggxLUTTexture .getTexture ());
      gl .uniform1i (uniforms .ggxLUTTexture, ggxLUTTextureUnit);

      if (uniforms .sheenTexture)
      {
         const
            sheenTexture      = lightNode .getSheenTexture (),
            charlieLUTTexture = browser .getLibraryTexture ("lut_charlie.png");

         const sheenTextureUnit = global
            ? this .sheenTextureUnit ??= browser .popGlobalTextureUnit ()
            : browser .popTextureUnit ();

         const charlieLUTTextureUnit = global
            ? this .charlieLUTTextureUnit ??= browser .popGlobalTextureUnit ()
            : browser .popTextureUnit ();

         gl .activeTexture (gl .TEXTURE0 + sheenTextureUnit);
         gl .bindTexture (gl .TEXTURE_CUBE_MAP, sheenTexture ?.getTexture () ?? browser .getDefaultTextureCube ());
         gl .uniform1i (uniforms .sheenTexture, sheenTextureUnit);

         gl .activeTexture (gl .TEXTURE0 + charlieLUTTextureUnit);
         gl .bindTexture (gl .TEXTURE_2D, charlieLUTTexture .getTexture ());
         gl .uniform1i (uniforms .charlieLUTTexture, charlieLUTTextureUnit);
      }

      if (shaderObject .hasEnvironmentLight (i, this))
         return;

      gl .uniform3f        (uniforms .color,                 ... lightNode .getColor ());
      gl .uniform1f        (uniforms .intensity,             lightNode .getIntensity ());
      gl .uniformMatrix3fv (uniforms .rotation, false,       this .rotationMatrix);
      gl .uniform1i        (uniforms .diffuseTextureLevels,  diffuseTexture ?.getLevels () ?? 0);
      gl .uniform1i        (uniforms .specularTextureLevels, specularTexture ?.getLevels () ?? 0);

      if (lightNode .traverseSpecular)
         gl .uniform3f (uniforms .flipX, 1, 1, 1);
      else
         gl .uniform3f (uniforms .flipX, -1, 1, 1);

      if (uniforms .sheenTexture)
      {
         const sheenTexture = lightNode .getSheenTexture ();

         gl .uniform1i (uniforms .sheenTextureLevels, sheenTexture ?.getLevels () ?? 0);
      }
   },
   dispose ()
   {
      if (this .global)
      {
         this .diffuseTextureUnit    = undefined;
         this .specularTextureUnit   = undefined;
         this .ggxLUTTextureUnit     = undefined;
         this .sheenTextureUnit      = undefined;
         this .charlieLUTTextureUnit = undefined;
      }

      this .modelViewMatrix .clear ();

      // Return container

      EnvironmentLights .push (this);
   },
});

function EnvironmentLight (executionContext)
{
   Lighting_X3DLightNode .call (this, executionContext);

   this .addType (Base_X3DConstants .EnvironmentLight);
}

Object .assign (Object .setPrototypeOf (EnvironmentLight .prototype, Lighting_X3DLightNode .prototype),
{
   initialize ()
   {
      Lighting_X3DLightNode .prototype .initialize .call (this);

      // Preload LUTs.
      this .getBrowser () .getLibraryTexture ("lut_ggx.png");

      this ._specularTexture .addInterest ("set_specularTexture__", this);

      this .set_specularTexture__ ();
   },
   getLightKey ()
   {
      return this .lightKey;
   },
   getDiffuseTexture ()
   {
      return this .generatedDiffuseTexture;
   },
   getSpecularTexture ()
   {
      return this .generatedSpecularTexture;
   },
   getSheenTexture ()
   {
      return this .generatedSheenTexture;
   },
   getLights ()
   {
      return EnvironmentLights;
   },
   set_specularTexture__ ()
   {
      if (this .traverseSpecular)
         this .specularTexture .removeUpdateCallback (this);

      this .specularTexture ?.removeInterest ("generateTextures", this);

      this .specularTexture  = Base_X3DCast (Base_X3DConstants .X3DEnvironmentTextureNode, this ._specularTexture);
      this .traverseSpecular = this .specularTexture ?.isRenderedTexture ();

      if (this .traverseSpecular)
         this .specularTexture .addUpdateCallback (this, () => this .generateTextures ());
      else
         this .specularTexture ?.addInterest ("generateTextures", this);

      this .setVisibleObject (this .traverseSpecular);
      this .generateTextures ();
   },
   generateTextures ()
   {
      this .generatedDiffuseTexture = (() =>
      {
         if (!this .specularTexture)
            return;

         // Render the texture.

         const browser = this .getBrowser ();

         if (browser .getBrowserOption ("Debug") && this .specularTexture .getSize () > 1)
            console .info ("Generating diffuse texture for EnvironmentLight.");

         return browser .filterEnvironmentTexture ({
            name: "GeneratedDiffuseTexture",
            texture: this .specularTexture,
            distribution: Lighting_Distribution .LAMBERTIAN,
            sampleCount: 2048,
            roughness: [0],
            flipX: !this .traverseSpecular,
            cachedNode: this .generatedDiffuseTexture,
         });
      })();

      this .generatedSpecularTexture = (() =>
      {
         if (!this .specularTexture)
            return;

         // Render the texture.

         const browser = this .getBrowser ();

         if (browser .getBrowserOption ("Debug") && this .specularTexture .getSize () > 1)
            console .info ("Generating specular texture for EnvironmentLight.");

         const
            levels    = this .specularTexture .getLevels (),
            roughness = Array .from ({ length: levels + 1 }, (_, i) => i / (levels || 1));

         return browser .filterEnvironmentTexture ({
            name: "GeneratedSpecularTexture",
            texture: this .specularTexture,
            distribution: Lighting_Distribution .GGX,
            sampleCount: 1024,
            roughness,
            flipX: !this .traverseSpecular,
            cachedNode: this .generatedSpecularTexture,
         });
      })();

      this .generatedSheenTexture = (() =>
      {
         if (!this .specularTexture)
            return;

         // Render the texture.

         const browser = this .getBrowser ();

         if (browser .getBrowserOption ("Debug") && this .specularTexture .getSize () > 1)
            console .info ("Generating sheen texture for EnvironmentLight.");

         const
            levels    = this .specularTexture .getLevels (),
            roughness = Array .from ({ length: levels + 1 }, (_, i) => Math .max (i / (levels || 1), 2, 0.000001));

         return browser .filterEnvironmentTexture ({
            name: "GeneratedSheenTexture",
            texture: this .specularTexture,
            distribution: Lighting_Distribution .CHARLIE,
            sampleCount: 64,
            roughness: roughness,
            flipX: !this .traverseSpecular,
            cachedNode: this .generatedSheenTexture,
         });
      })();

      this .lightKey = (() =>
      {
         const
            diffuseLinear  = this .getDiffuseTexture ()  ?.isLinear () ? 1 : 0,
            specularLinear = this .getSpecularTexture () ?.isLinear () ? 1 : 0,
            sheenLinear    = this .getSheenTexture ()    ?.isLinear () ? 1 : 0;

         return `[2.${diffuseLinear}.${specularLinear}${sheenLinear}]`;
      })();
   },
   traverse (type, renderObject)
   {
      if (!this .traverseSpecular)
         return;

      if (this .specularTexture ._update .getValue () === "NONE")
         return;

      if (!renderObject .isIndependent ())
         return;

      const modelViewMatrix = renderObject .getModelViewMatrix ();

      modelViewMatrix .push ();
      modelViewMatrix .translate (this ._origin .getValue ());

      this .specularTexture .traverse (type, renderObject);

      modelViewMatrix .pop ();
   },
});

Object .defineProperties (EnvironmentLight,
{
   // X_ITE introduces this node in 4.0, but X3D specification adds this node in 4.1.
   ... Core_X3DNode .getStaticProperties ("EnvironmentLight", "Lighting", 3, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "global",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "on",                  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",               new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",           new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "ambientIntensity",    new x_ite_Fields .SFFloat ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "origin",              new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "rotation",            new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "diffuseCoefficients", new x_ite_Fields .MFFloat ()), // not supported
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "diffuseTexture",      new x_ite_Fields .SFNode ()),  // not supported
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "specularTexture",     new x_ite_Fields .SFNode ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadows",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowColor",         new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowIntensity",     new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowBias",          new x_ite_Fields .SFFloat (0.005)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "shadowMapSize",       new x_ite_Fields .SFInt32 (1024)),
      ]),
      enumerable: true,
   },
});

const EnvironmentLight_default_ = EnvironmentLight;
;

/* harmony default export */ const Lighting_EnvironmentLight = (Namespace/* default */.A .add ("EnvironmentLight", EnvironmentLight_default_));
;// ./src/x_ite/Components/Lighting/PointLight.js

















// Shadow map layout
// Compact layout:
//
// xzXZ      Char: Axis
// yyYY      Case: Sign

const orientationMatrices = [
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .X_AXIS,          Numbers_Vector3 .Z_AXIS)), // left
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .NEGATIVE_X_AXIS, Numbers_Vector3 .Z_AXIS)), // right
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .NEGATIVE_Z_AXIS, Numbers_Vector3 .Z_AXIS)), // front
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .Z_AXIS,          Numbers_Vector3 .Z_AXIS)), // back
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .Y_AXIS,          Numbers_Vector3 .Z_AXIS)), // bottom
   new Numbers_Matrix4 () .setRotation (Numbers_Rotation4 .fromVectors (Numbers_Vector3 .NEGATIVE_Y_AXIS, Numbers_Vector3 .Z_AXIS)), // top
];

const viewports = [
   new Numbers_Vector4 (0,    0.5, 0.25, 0.5), // left
   new Numbers_Vector4 (0.5,  0.5, 0.25, 0.5), // right
   new Numbers_Vector4 (0.75, 0.5, 0.25, 0.5), // front
   new Numbers_Vector4 (0.25, 0.5, 0.25, 0.5), // back
   new Numbers_Vector4 (0.0,  0,   0.5,  0.5), // bottom
   new Numbers_Vector4 (0.5,  0,   0.5,  0.5), // top
];

const PointLights = Utility_ObjectCache (PointLightContainer);

function PointLightContainer ()
{
   this .location                      = new Numbers_Vector3 ();
   this .matrixArray                   = new Float32Array (9);
   this .shadowBuffer                  = null;
   this .viewVolume                    = new Geometry_ViewVolume ();
   this .viewport                      = new Numbers_Vector4 ();
   this .projectionMatrix              = new Numbers_Matrix4 ();
   this .modelViewMatrix               = new Utility_MatrixStack (Numbers_Matrix4);
   this .modelMatrix                   = new Numbers_Matrix4 ();
   this .invLightSpaceMatrix           = new Numbers_Matrix4 ();
   this .invLightSpaceProjectionMatrix = new Numbers_Matrix4 ();
   this .shadowMatrix                  = new Numbers_Matrix4 ();
   this .shadowMatrixArray             = new Float32Array (16);
   this .rotation                      = new Numbers_Rotation4 ();
   this .rotationMatrix                = new Numbers_Matrix4 ();
   this .textureUnit                   = undefined;
}

Object .assign (PointLightContainer .prototype,
{
   set (lightNode, groupNode, modelViewMatrix)
   {
      const shadowMapSize = lightNode .getShadowMapSize ();

      this .browser           = lightNode .getBrowser ();
      this .lightNode         = lightNode;
      this .groupNode         = groupNode;
      this .global            = lightNode .getGlobal ();
      this .shadowMapRendered = false;

      this .modelViewMatrix .push (modelViewMatrix);

      // Get shadow buffer from browser.

      if (lightNode .getShadowIntensity () && shadowMapSize)
         this .shadowBuffer = this .browser .popShadowBuffer (shadowMapSize);
   },
   renderShadowMap (renderObject)
   {
      if (!this .shadowBuffer)
         return;

      if (this .shadowMapRendered)
         return;

      this .shadowMapRendered = true;

      const
         lightNode           = this .lightNode,
         cameraSpaceMatrix   = renderObject .getCameraSpaceMatrix () .get (),
         modelMatrix         = this .modelMatrix .assign (this .modelViewMatrix .get ()) .multRight (cameraSpaceMatrix),
         invLightSpaceMatrix = this .invLightSpaceMatrix .assign (this .global ? modelMatrix : Numbers_Matrix4 .IDENTITY);

      invLightSpaceMatrix .translate (lightNode .getLocation ());
      invLightSpaceMatrix .inverse ();

      const shadowMapSize = lightNode .getShadowMapSize ();

      this .shadowBuffer .bind ();

      for (let i = 0; i < 6; ++ i)
      {
         const
            v                = viewports [i],
            viewport         = this .viewport .set (v [0] * shadowMapSize, v [1] * shadowMapSize, v [2] * shadowMapSize, v [3] * shadowMapSize),
            projectionMatrix = Geometry_Camera .perspective2 (Math_Algorithm .radians (90), 0.125, 10_000, viewport [2], viewport [3], this .projectionMatrix); // Use higher far value for better precision.

         renderObject .getViewVolumes      () .push (this .viewVolume .set (projectionMatrix, viewport, viewport));
         renderObject .getProjectionMatrix () .push (this .projectionMatrix);
         renderObject .getModelViewMatrix  () .push (orientationMatrices [i]);
         renderObject .getModelViewMatrix  () .multLeft (invLightSpaceMatrix);

         renderObject .render (Rendering_TraverseType .SHADOW, this .groupNode .traverse, this .groupNode);

         renderObject .getModelViewMatrix  () .pop ();
         renderObject .getProjectionMatrix () .pop ();
         renderObject .getViewVolumes () .pop ();
      }

      if (!this .global)
         invLightSpaceMatrix .multLeft (modelMatrix .inverse ());

      this .invLightSpaceProjectionMatrix .assign (invLightSpaceMatrix);
   },
   setGlobalVariables (renderObject)
   {
      const modelViewMatrix = this .modelViewMatrix .get ();

      this .matrixArray .set (modelViewMatrix .submatrix .inverse ());

      modelViewMatrix .multVecMatrix (this .location .assign (this .lightNode ._location .getValue ()));

      if (!this .shadowBuffer)
         return;

      this .shadowMatrix
         .assign (renderObject .getView () ?.inverse ?? Numbers_Matrix4 .IDENTITY)
         .multRight (renderObject .getCameraSpaceMatrixArray ())
         .multRight (this .invLightSpaceProjectionMatrix);

      this .shadowMatrixArray .set (this .shadowMatrix);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         i        = shaderObject .numLights ++,
         uniforms = shaderObject .x3d_Light [i];

      if (!uniforms)
         return;

      if (this .shadowBuffer)
      {
         const textureUnit = this .global
            ? this .textureUnit ??= this .browser .popGlobalTextureUnit ()
            : this .browser .popTextureUnit ();

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (gl .TEXTURE_2D, this .shadowBuffer .getDepthTexture ());
         gl .uniform1i (uniforms .shadowMap, textureUnit);
      }

      if (shaderObject .hasLight (i, this))
         return;

      const { lightNode, location } = this;

      gl .uniform1i        (uniforms .type,             2);
      gl .uniform3f        (uniforms .color,            ... lightNode .getColor ());
      gl .uniform1f        (uniforms .intensity,        lightNode .getIntensity ());
      gl .uniform1f        (uniforms .ambientIntensity, lightNode .getAmbientIntensity ());
      gl .uniform3fv       (uniforms .attenuation,      lightNode .getAttenuation ());
      gl .uniform3f        (uniforms .location,         ... location);
      gl .uniform1f        (uniforms .radius,           lightNode .getRadius ());
      gl .uniformMatrix3fv (uniforms .matrix, false,    this .matrixArray);

      if (this .shadowBuffer)
      {
         gl .uniform3f        (uniforms .shadowColor,         ... lightNode .getShadowColor ());
         gl .uniform1f        (uniforms .shadowIntensity,     lightNode .getShadowIntensity ());
         gl .uniform1f        (uniforms .shadowBias,          lightNode .getShadowBias ());
         gl .uniformMatrix4fv (uniforms .shadowMatrix, false, this .shadowMatrixArray);
         gl .uniform1i        (uniforms .shadowMapSize,       lightNode .getShadowMapSize ());
      }
      else
      {
         gl .uniform1f (uniforms .shadowIntensity, 0);
      }
   },
   dispose ()
   {
      const { shadowBuffer } = this;

      if (shadowBuffer)
      {
         const { browser, global } = this;

         browser .pushShadowBuffer (shadowBuffer);

         this .shadowBuffer = null;

         if (global)
            this .textureUnit = undefined;
      }

      this .modelViewMatrix .clear ();

      // Return container

      PointLights .push (this);
   },
});

function PointLight (executionContext)
{
   Lighting_X3DLightNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PointLight);

   // Units

   this ._location .setUnit ("length");
   this ._radius   .setUnit ("length");

   // Private properties

   this .attenuation = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (PointLight .prototype, Lighting_X3DLightNode .prototype),
{
   initialize ()
   {
      Lighting_X3DLightNode .prototype .initialize .call (this);

      this ._attenuation .addInterest ("set_attenuation__", this);

      this .set_attenuation__ ();
   },
   set_attenuation__ ()
   {
      const attenuation = this ._attenuation .getValue ();

      this .attenuation [0] = Math .max (0, attenuation .x);
      this .attenuation [1] = Math .max (0, attenuation .y);
      this .attenuation [2] = Math .max (0, attenuation .z);
   },
   getAttenuation ()
   {
      return this .attenuation;
   },
   getLocation ()
   {
      return this ._location .getValue ();
   },
   getRadius ()
   {
      // Negative values mean infinity.
      return this ._radius .getValue ();
   },
   getLights ()
   {
      return PointLights;
   },
});

Object .defineProperties (PointLight,
{
   ... Core_X3DNode .getStaticProperties ("PointLight", "Lighting", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "global",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "on",               new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",            new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "ambientIntensity", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attenuation",      new x_ite_Fields .SFVec3f (1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "location",         new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "radius",           new x_ite_Fields .SFFloat (100)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadows",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowColor",     new x_ite_Fields .SFColor ()),      // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowIntensity", new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowBias",      new x_ite_Fields .SFFloat (0.005)), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "shadowMapSize",   new x_ite_Fields .SFInt32 (1024)),  // skip test
      ]),
      enumerable: true,
   },
});

const PointLight_default_ = PointLight;
;

/* harmony default export */ const Lighting_PointLight = (Namespace/* default */.A .add ("PointLight", PointLight_default_));
;// ./src/x_ite/Components/Lighting/SpotLight.js


















const SpotLights = Utility_ObjectCache (SpotLightContainer);

function SpotLightContainer ()
{
   this .location                      = new Numbers_Vector3 ();
   this .direction                     = new Numbers_Vector3 ();
   this .matrixArray                   = new Float32Array (9);
   this .renderShadow                  = true;
   this .shadowBuffer                  = null;
   this .bbox                          = new Geometry_Box3 ();
   this .viewVolume                    = new Geometry_ViewVolume ();
   this .viewport                      = new Numbers_Vector4 ();
   this .projectionMatrix              = new Numbers_Matrix4 ();
   this .modelViewMatrix               = new Utility_MatrixStack (Numbers_Matrix4);
   this .modelMatrix                   = new Numbers_Matrix4 ();
   this .invLightSpaceMatrix           = new Numbers_Matrix4 ();
   this .invLightSpaceProjectionMatrix = new Numbers_Matrix4 ();
   this .shadowMatrix                  = new Numbers_Matrix4 ();
   this .shadowMatrixArray             = new Float32Array (16);
   this .rotation                      = new Numbers_Rotation4 ();
   this .lightBBoxMin                  = new Numbers_Vector3 ();
   this .lightBBoxMax                  = new Numbers_Vector3 ();
   this .textureUnit                   = undefined;
}

Object .assign (SpotLightContainer .prototype,
{
   set (lightNode, groupNode, modelViewMatrix)
   {
      const shadowMapSize = lightNode .getShadowMapSize ();

      this .browser           = lightNode .getBrowser ();
      this .lightNode         = lightNode;
      this .groupNode         = groupNode;
      this .global            = lightNode .getGlobal ();
      this .shadowMapRendered = false;

      this .modelViewMatrix .push (modelViewMatrix);

      // Get shadow buffer from browser.

      if (lightNode .getShadowIntensity () && shadowMapSize)
         this .shadowBuffer = this .browser .popShadowBuffer (shadowMapSize);
   },
   renderShadowMap (renderObject)
   {
      if (!this .shadowBuffer)
         return;

      if (this .shadowMapRendered)
         return;

      this .shadowMapRendered = true;

      const
         lightNode            = this .lightNode,
         cameraSpaceMatrix    = renderObject .getCameraSpaceMatrix () .get (),
         modelMatrix          = this .modelMatrix .assign (this .modelViewMatrix .get ()) .multRight (cameraSpaceMatrix),
         invLightSpaceMatrix  = this .invLightSpaceMatrix .assign (this .global ? modelMatrix : Numbers_Matrix4 .IDENTITY);

      invLightSpaceMatrix .translate (lightNode .getLocation ());
      invLightSpaceMatrix .rotate (this .rotation .setVectors (Numbers_Vector3 .Z_AXIS, this .direction .assign (lightNode .getDirection ()) .negate ()));
      invLightSpaceMatrix .inverse ();

      const
         groupBBox = this .groupNode .getSubBBox (this .bbox, true), // Group bbox.
         lightBBox = groupBBox .multRight (invLightSpaceMatrix);     // Group bbox from the perspective of the light.

      lightBBox .getExtents (this .lightBBoxMin, this .lightBBoxMax); // Result not used, but arguments.

      const
         shadowMapSize    = lightNode .getShadowMapSize (),
         farValue         = Math .min (lightNode .getRadius (), -this .lightBBoxMin .z),
         viewport         = this .viewport .set (0, 0, shadowMapSize, shadowMapSize),
         projectionMatrix = Geometry_Camera .perspective (lightNode .getCutOffAngle () * 2, 0.125, Math .max (10000, farValue), shadowMapSize, shadowMapSize, this .projectionMatrix); // Use higher far value for better precision.

      this .renderShadow = farValue > 0;

      this .shadowBuffer .bind ();

      renderObject .getViewVolumes      () .push (this .viewVolume .set (projectionMatrix, viewport, viewport));
      renderObject .getProjectionMatrix () .push (projectionMatrix);
      renderObject .getModelViewMatrix  () .push (invLightSpaceMatrix);

      renderObject .render (Rendering_TraverseType .SHADOW, this .groupNode .traverse, this .groupNode);

      renderObject .getModelViewMatrix  () .pop ();
      renderObject .getProjectionMatrix () .pop ();
      renderObject .getViewVolumes      () .pop ();

      if (!this .global)
         invLightSpaceMatrix .multLeft (modelMatrix .inverse ());

      this .invLightSpaceProjectionMatrix .assign (invLightSpaceMatrix) .multRight (projectionMatrix) .multRight (lightNode .getBiasMatrix ());
   },
   setGlobalVariables (renderObject)
   {
      const
         lightNode       = this .lightNode,
         modelViewMatrix = this .modelViewMatrix .get ();

      this .matrixArray .set (modelViewMatrix .submatrix .inverse ());

      modelViewMatrix .multVecMatrix (this .location  .assign (lightNode ._location  .getValue ()));
      modelViewMatrix .multDirMatrix (this .direction .assign (lightNode ._direction .getValue ())) .normalize ();

      if (!this .shadowBuffer)
         return;

      this .shadowMatrix
         .assign (renderObject .getView () ?.inverse ?? Numbers_Matrix4 .IDENTITY)
         .multRight (renderObject .getCameraSpaceMatrixArray ())
         .multRight (this .invLightSpaceProjectionMatrix);

      this .shadowMatrixArray .set (this .shadowMatrix);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         i        = shaderObject .numLights ++,
         uniforms = shaderObject .x3d_Light [i];

      if (!uniforms)
         return;

      if (this .shadowBuffer)
      {
         const textureUnit = this .global
            ? this .textureUnit ??= this .browser .popGlobalTextureUnit ()
            : this .browser .popTextureUnit ();

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (gl .TEXTURE_2D, this .shadowBuffer .getDepthTexture ());
         gl .uniform1i (uniforms .shadowMap, textureUnit);
      }

      if (shaderObject .hasLight (i, this))
         return;

      const { lightNode, location, direction } = this;

      gl .uniform1i        (uniforms .type,             3);
      gl .uniform3f        (uniforms .color,            ... lightNode .getColor ());
      gl .uniform1f        (uniforms .intensity,        lightNode .getIntensity ());
      gl .uniform1f        (uniforms .ambientIntensity, lightNode .getAmbientIntensity ());
      gl .uniform3fv       (uniforms .attenuation,      lightNode .getAttenuation ());
      gl .uniform3f        (uniforms .location,         ... location);
      gl .uniform3f        (uniforms .direction,        ... direction);
      gl .uniform1f        (uniforms .radius,           lightNode .getRadius ());
      gl .uniform1f        (uniforms .beamWidth,        lightNode .getBeamWidth ());
      gl .uniform1f        (uniforms .cutOffAngle,      lightNode .getCutOffAngle ());
      gl .uniformMatrix3fv (uniforms .matrix, false,    this .matrixArray);

      if (this .renderShadow && this .shadowBuffer)
      {
         gl .uniform3f        (uniforms .shadowColor,         ... lightNode .getShadowColor ());
         gl .uniform1f        (uniforms .shadowIntensity,     lightNode .getShadowIntensity ());
         gl .uniform1f        (uniforms .shadowBias,          lightNode .getShadowBias ());
         gl .uniformMatrix4fv (uniforms .shadowMatrix, false, this .shadowMatrixArray);
         gl .uniform1i        (uniforms .shadowMapSize,       lightNode .getShadowMapSize ());
      }
      else
      {
         gl .uniform1f (uniforms .shadowIntensity, 0);
      }
   },
   dispose ()
   {
      const { shadowBuffer } = this;

      if (shadowBuffer)
      {
         const { browser, global } = this;

         browser .pushShadowBuffer (shadowBuffer);

         this .shadowBuffer = null;

         if (global)
            this .textureUnit = undefined;
      }

      this .modelViewMatrix .clear ();

      // Return container

      SpotLights .push (this);
   },
});

function SpotLight (executionContext)
{
   Lighting_X3DLightNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SpotLight);

   // Units

   this ._location    .setUnit ("length");
   this ._radius      .setUnit ("length");
   this ._beamWidth   .setUnit ("angle");
   this ._cutOffAngle .setUnit ("angle");

   // Legacy

   if (executionContext .getSpecificationVersion () == 3.3)
   {
      this ._beamWidth   = 0.785398;
      this ._cutOffAngle = 1.5708;
   }
   else if (executionContext .getSpecificationVersion () <= 3.2)
   {
      this ._beamWidth   = 1.5708;
      this ._cutOffAngle = 0.785398;
   }

   // Private properties

   this .attenuation = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (SpotLight .prototype, Lighting_X3DLightNode .prototype),
{
   initialize ()
   {
      Lighting_X3DLightNode .prototype .initialize .call (this);

      this ._attenuation .addInterest ("set_attenuation__", this);

      this .set_attenuation__ ();
   },
   set_attenuation__ ()
   {
      const attenuation = this ._attenuation .getValue ();

      this .attenuation [0] = Math .max (0, attenuation .x);
      this .attenuation [1] = Math .max (0, attenuation .y);
      this .attenuation [2] = Math .max (0, attenuation .z);
   },
   getAttenuation ()
   {
      return this .attenuation;
   },
   getLocation ()
   {
      return this ._location .getValue ();
   },
   getRadius ()
   {
      // Negative values mean infinity.
      return this ._radius .getValue ();
   },
   getBeamWidth ()
   {
      // If the beamWidth is greater than the cutOffAngle, beamWidth is defined to be equal to the cutOffAngle.

      const
         beamWidth   = this ._beamWidth .getValue (),
         cutOffAngle = this .getCutOffAngle ();

      if (beamWidth > cutOffAngle)
         return cutOffAngle;

      return Math_Algorithm .clamp (beamWidth, 0, Math .PI / 2);
   },
   getCutOffAngle ()
   {
      return Math_Algorithm .clamp (this ._cutOffAngle .getValue (), 0, Math .PI / 2);
   },
   getLights ()
   {
      return SpotLights;
   },
});

Object .defineProperties (SpotLight,
{
   ... Core_X3DNode .getStaticProperties ("SpotLight", "Lighting", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "global",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "on",               new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",            new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "ambientIntensity", new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attenuation",      new x_ite_Fields .SFVec3f (1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "location",         new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "direction",        new x_ite_Fields .SFVec3f (0, 0, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "radius",           new x_ite_Fields .SFFloat (100)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "beamWidth",        new x_ite_Fields .SFFloat (0.589049)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "cutOffAngle",      new x_ite_Fields .SFFloat (1.570796)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadows",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowColor",     new x_ite_Fields .SFColor ()),      // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowIntensity", new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "shadowBias",      new x_ite_Fields .SFFloat (0.005)), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "shadowMapSize",   new x_ite_Fields .SFInt32 (1024)),  // skip test
      ]),
      enumerable: true,
   },
});

const SpotLight_default_ = SpotLight;
;

/* harmony default export */ const Lighting_SpotLight = (Namespace/* default */.A .add ("SpotLight", SpotLight_default_));
;// ./src/x_ite/Components/LightingComponent.js






const LightingComponent_default_ = {
   name: "Lighting",
   concreteNodes:
   [
      Lighting_DirectionalLight,
      Lighting_EnvironmentLight,
      Lighting_PointLight,
      Lighting_SpotLight,
   ],
   abstractNodes:
   [
      Lighting_X3DLightNode,
   ],
};
;

/* harmony default export */ const LightingComponent = (Namespace/* default */.A .add ("LightingComponent", LightingComponent_default_));
;// ./src/x_ite/Components/Navigation/Billboard.js











function Billboard (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Billboard);

   // Private properties

   this .matrix = new Numbers_Matrix4 ();
}

Object .assign (Object .setPrototypeOf (Billboard .prototype, Grouping_X3DGroupingNode .prototype),
{
   getBBox (bbox, shadows)
   {
      if (this .isDefaultBBoxSize ())
         return this .getSubBBox (bbox, shadows) .multRight (this .matrix);

      return bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
   },
   getMatrix ()
   {
      return this .matrix;
   },
   rotate: (() =>
   {
      const
         inverseModelViewMatrix = new Numbers_Matrix4 (),
         viewerYAxis            = new Numbers_Vector3 (),
         y                      = new Numbers_Vector3 (),
         N1                     = new Numbers_Vector3 (),
         N2                     = new Numbers_Vector3 (),
         rotation               = new Numbers_Rotation4 ();

      return function (modelViewMatrix)
      {
         inverseModelViewMatrix .assign (modelViewMatrix) .inverse ();

         const billboardToViewer = inverseModelViewMatrix .origin .normalize (); // Normalized to get work with Geo

         if (this ._axisOfRotation .getValue () .equals (Numbers_Vector3 .ZERO))
         {
            inverseModelViewMatrix .multDirMatrix (viewerYAxis .assign (Numbers_Vector3 .Y_AXIS)) .normalize (); // Normalized to get work with Geo

            const
               z = billboardToViewer,
               x = viewerYAxis .cross (billboardToViewer);

            y .assign (billboardToViewer) .cross (x);

            // Compose rotation

            x .normalize ();
            y .normalize ();

            this .matrix .set (... x, 0,
                               ... y, 0,
                               ... z, 0,
                               0, 0, 0, 1);
         }
         else
         {
            N1 .assign (this ._axisOfRotation .getValue ()) .cross (billboardToViewer); // Normal vector of plane as in specification
            N2 .assign (this ._axisOfRotation .getValue ()) .cross (Numbers_Vector3 .Z_AXIS);    // Normal vector of plane between axisOfRotation and zAxis

            this .matrix .setRotation (rotation .setVectors (N2, N1));                // Rotate zAxis in plane
         }

         return this .matrix;
      };
   })(),
   traverse (type, renderObject)
   {
      const modelViewMatrix = renderObject .getModelViewMatrix ();

      modelViewMatrix .push ();

      switch (type)
      {
         case Rendering_TraverseType .CAMERA:
         case Rendering_TraverseType .PICKING:
         case Rendering_TraverseType .DEPTH:
         case Rendering_TraverseType .SHADOW:
            // No clone support for shadows, generated cube map texture, and bbox
            modelViewMatrix .multLeft (this .matrix);
            break;
         default:
            modelViewMatrix .multLeft (this .rotate (modelViewMatrix .get ()));
            break;
      }

      Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);

      modelViewMatrix .pop ();
   },
});

Object .defineProperties (Billboard,
{
   ... Core_X3DNode .getStaticProperties ("Billboard", "Navigation", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "axisOfRotation", new x_ite_Fields .SFVec3f (0, 1, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",       new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Billboard_default_ = Billboard;
;

/* harmony default export */ const Navigation_Billboard = (Namespace/* default */.A .add ("Billboard", Billboard_default_));
;// ./src/x_ite/Components/Navigation/Collision.js










function Collision (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);
   Core_X3DSensorNode   .call (this, executionContext);

   this .addType (Base_X3DConstants .Collision);

   // Legacy

   if (executionContext .getSpecificationVersion () == 2.0)
      this .addAlias ("collide", this ._enabled); // VRML2
}

Object .assign (Object .setPrototypeOf (Collision .prototype, Grouping_X3DGroupingNode .prototype),
   Core_X3DSensorNode .prototype,
{
   initialize ()
   {
      Grouping_X3DGroupingNode .prototype .initialize .call (this);
      // X3DSensorNode .prototype .initialize .call (this); // We can only call the base of a *Objects.

      this .getLive () .addInterest ("set_live__",     this);
      this ._enabled   .addInterest ("set_enabled__",  this);
      this ._proxy     .addInterest ("set_children__", this);

      this .set_live__ ();
   },
   set_live__ ()
   {
      if (this .getLive () .getValue () && this ._enabled .getValue ())
         this .getBrowser () .addCollision (this);

      else
         this .getBrowser () .removeCollision (this);
   },
   set_enabled__ ()
   {
      if (!this ._enabled .getValue ())
         this .set_active__ (false);

      this .set_live__ ();
      this .set_children__ ();
   },
   set_active__ (value)
   {
      if (this ._isActive .getValue () === value)
         return;

      this ._isActive = value;

      if (value)
         this ._collideTime = this .getBrowser () .getCurrentTime ();
   },
   set_collisionObjects__ ()
   {
      const proxyNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, this ._proxy);

      if (!this ._enabled .getValue ())
      {
         this .collisionObjects .clear ();
      }
      else if (proxyNode)
      {
         this .collisionObjects .clear ();
         this .collisionObjects .add (proxyNode);
      }

      Grouping_X3DGroupingNode .prototype .set_collisionObjects__ .call (this);
   },
   traverse (type, renderObject)
   {
      switch (type)
      {
         case Rendering_TraverseType .COLLISION:
         {
            const collisions = renderObject .getCollisions ();

            collisions .push (this);

            Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);

            collisions .pop ();
            return;
         }
         default:
         {
            Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);
            return;
         }
      }
   },
   dispose ()
   {
      // X3DSensorNode .prototype .dispose .call (this); // We can only call the base of a *Objects.
      Grouping_X3DGroupingNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (Collision,
{
   ... Core_X3DNode .getStaticProperties ("Collision", "Navigation", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",       new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",    new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enabled",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",       new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "collideTime",    new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",       new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",     new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "proxy",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",    new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren", new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",       new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Collision_default_ = Collision;
;

/* harmony default export */ const Navigation_Collision = (Namespace/* default */.A .add ("Collision", Collision_default_));
;// ./src/x_ite/Components/Navigation/LOD.js










function LOD (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .LOD);

   this .setVisibleObject (true);

   // Units

   this ._center .setUnit ("length");
   this ._range  .setUnit ("length");

   // Legacy

   if (executionContext .getSpecificationVersion () == 2.0)
      this .addAlias ("level", this ._children); // VRML2

   // Private properties

   this .frameRate = 60;
}

Object .assign (Object .setPrototypeOf (LOD .prototype, Grouping_X3DGroupingNode .prototype),
{
   addChildren ()
   { },
   removeChildren ()
   { },
   set_addChildren__ ()
   {
      Grouping_X3DGroupingNode .prototype .set_addChildren__ .call (this);

      this .set_children__ ();
   },
   set_removeChildren__ ()
   {
      Grouping_X3DGroupingNode .prototype .set_removeChildren__ .call (this);

      this .set_children__ ();
   },
   set_children__ ()
   {
      this .clearChildren ();

      // Add single child.

      const level = Math .min (this ._level_changed .getValue (), this ._children .length - 1);

      if (level >= 0 && level < this ._children .length)
         this .addChild (this ._children [level]);

      this .set_objects__ ();
   },
   set_visibleObjects__ ()
   { },
   getLevel: (() =>
   {
      const
         FRAMES         = 180, // Number of frames after which a level change takes in affect.
         FRAME_RATE_MIN = 20,  // Lowest level of detail.
         FRAME_RATE_MAX = 55;  // Highest level of detail.

      return function (modelViewMatrix)
      {
         if (this ._range .length === 0)
         {
            const browser = this .getBrowser ();

            this .frameRate = ((FRAMES - 1) * this .frameRate + browser .currentFrameRate) / FRAMES;

            const size = this ._children .length;

            switch (size)
            {
               case 0:
                  return -1;
               case 1:
                  return 0;
               case 2:
                  return +(this .frameRate > FRAME_RATE_MAX);
               default:
               {
                  const fraction = 1 - Math_Algorithm .clamp ((this .frameRate - FRAME_RATE_MIN) / (FRAME_RATE_MAX - FRAME_RATE_MIN), 0, 1);

                  return Math .min (Math .floor (fraction * size), size - 1);
               }
            }
         }

         const distance = modelViewMatrix .translate (this ._center .getValue ()) .origin .norm ();

         return Math_Algorithm .upperBound (this ._range, 0, this ._range .length, distance);
      };
   })(),
   changeLevel: (() =>
   {
      const modelViewMatrix = new Numbers_Matrix4 ();

      return function (renderObject)
      {
         const currentLevel = this ._level_changed .getValue ();

         let level = this .getLevel (modelViewMatrix .assign (renderObject .getModelViewMatrix () .get ()));

         if (this ._forceTransitions .getValue ())
         {
            if (level > currentLevel)
               level = currentLevel + 1;

            else if (level < currentLevel)
               level = currentLevel - 1;
         }

         if (level === currentLevel)
            return;

         this ._level_changed = level;

         this .set_children__ ();
      };
   })(),
   traverse (type, renderObject)
   {
      if (type === Rendering_TraverseType .DISPLAY)
         this .changeLevel (renderObject);

      Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);
   },
});

Object .defineProperties (LOD,
{
   ... Core_X3DNode .getStaticProperties ("LOD", "Navigation", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "forceTransitions", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "center",           new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "range",            new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "level_changed",    new x_ite_Fields .SFInt32 (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",         new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",       new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",      new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren",   new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",         new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const LOD_default_ = LOD;
;

/* harmony default export */ const Navigation_LOD = (Namespace/* default */.A .add ("LOD", LOD_default_));
;// ./src/x_ite/Components/Navigation/ViewpointGroup.js











function ViewpointGroup (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ViewpointGroup);

   this .setCameraObject  (true);
   this .setVisibleObject (true);

   // Units

   this ._size   .setUnit ("length");
   this ._center .setUnit ("length");

   // Private properties

   this .proximitySensor = new EnvironmentalSensor_ProximitySensor (executionContext);
   this .cameraObjects   = [ ];
   this .viewpointGroups = [ ];
}

Object .assign (Object .setPrototypeOf (ViewpointGroup .prototype, Core_X3DChildNode .prototype),
{
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this ._size     .addInterest ("set_size__",     this);
      this ._children .addInterest ("set_children__", this);

      this ._size   .addFieldInterest (this .proximitySensor ._size);
      this ._center .addFieldInterest (this .proximitySensor ._center);

      this .proximitySensor ._isActive .addInterest ("set_active__", this);

      this .proximitySensor ._size   = this ._size;
      this .proximitySensor ._center = this ._center;

      this .proximitySensor .setup ();

      this .set_size__ ();
      this .set_children__ ();
   },
   getDisplayed ()
   {
      return this .displayed;
   },
   set_size__ ()
   {
      this .proximitySensor ._enabled = !this ._size .getValue () .equals (Numbers_Vector3 .ZERO);

      this .set_active__ ();
   },
   set_active__ ()
   {
      const proximitySensor = this .proximitySensor;

      this .active    = proximitySensor ._isActive .getValue () || !proximitySensor ._enabled .getValue ();
      this .displayed = this ._displayed .getValue () && this .active;
   },
   set_children__ ()
   {
      this .cameraObjects   .length = 0;
      this .viewpointGroups .length = 0;

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DViewpointNode:
               {
                  this .cameraObjects .push (childNode);
                  break;
               }
               case Base_X3DConstants .ViewpointGroup:
               {
                  this .cameraObjects   .push (childNode);
                  this .viewpointGroups .push (childNode);
                  break;
               }
               default:
                  continue;
            }

            break;
         }
      }

      this .setCameraObject  (this .cameraObjects .length);
      this .setVisibleObject (this .cameraObjects .length);
   },
   traverse (type, renderObject)
   {
      this .proximitySensor .traverse (type, renderObject);

      switch (type)
      {
         case Rendering_TraverseType .CAMERA:
         {
            renderObject .getViewpointGroups () .push (this);

            for (const cameraObject of this .cameraObjects)
               cameraObject .traverse (type, renderObject);

            renderObject .getViewpointGroups () .pop ();
            return;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            if (this .active)
            {
               for (const viewpointGroup of this .viewpointGroups)
                  viewpointGroup .traverse (type, renderObject);
            }

            return;
         }
      }
   },
});

Object .defineProperties (ViewpointGroup,
{
   ... Core_X3DNode .getStaticProperties ("ViewpointGroup", "Navigation", 3, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "displayed",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "retainUserOffsets", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "size",              new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "center",            new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",          new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ViewpointGroup_default_ = ViewpointGroup;
;

/* harmony default export */ const Navigation_ViewpointGroup = (Namespace/* default */.A .add ("ViewpointGroup", ViewpointGroup_default_));
;// ./src/x_ite/Components/NavigationComponent.js









const NavigationComponent_default_ = {
   name: "Navigation",
   concreteNodes:
   [
      Navigation_Billboard,
      Navigation_Collision,
      Navigation_LOD,
      Navigation_NavigationInfo,
      Navigation_OrthoViewpoint,
      Navigation_Viewpoint,
      Navigation_ViewpointGroup,
   ],
   abstractNodes:
   [
      Navigation_X3DViewpointNode,
   ],
};
;

/* harmony default export */ const NavigationComponent = (Namespace/* default */.A .add ("NavigationComponent", NavigationComponent_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/X3DPointingDeviceSensorNode.js







const PointingDeviceSensors = Utility_ObjectCache (PointingDeviceSensorContainer);

function PointingDeviceSensorContainer ()
{
   this .node             = null;
   this .hit              = null;
   this .modelViewMatrix  = new Numbers_Matrix4 ();
   this .projectionMatrix = new Numbers_Matrix4 ();
   this .viewport         = new Numbers_Vector4 ();
}

Object .assign (PointingDeviceSensorContainer .prototype,
{
   set (node, modelViewMatrix, projectionMatrix, viewport)
   {
      this .node = node;

      this .modelViewMatrix  .assign (modelViewMatrix);
      this .projectionMatrix .assign (projectionMatrix);
      this .viewport         .assign (viewport);
   },
   set_over__ (over, hit)
   {
      this .node .set_over__ (over, hit, this .modelViewMatrix, this .projectionMatrix, this .viewport);
   },
   set_active__ (active, hit)
   {
      this .node .set_active__ (active, hit, this .modelViewMatrix, this .projectionMatrix, this .viewport);
   },
   set_motion__ (hit)
   {
      this .node .set_motion__ (hit, this .modelViewMatrix, this .projectionMatrix, this .viewport);
   },
   dispose ()
   {
      this .hit = null;

      // Return container

      PointingDeviceSensors .push (this);
   },
});

function X3DPointingDeviceSensorNode (executionContext)
{
   Core_X3DSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DPointingDeviceSensorNode);
}

Object .assign (Object .setPrototypeOf (X3DPointingDeviceSensorNode .prototype, Core_X3DSensorNode .prototype),
{
   initialize ()
   {
      Core_X3DSensorNode .prototype .initialize .call (this);

      this .getLive () .addInterest ("set_live__", this);

      this ._enabled .addInterest ("set_live__", this);

      this .set_live__ ();
   },
   set_live__ ()
   {
      if (this .getLive () .getValue () && this ._enabled .getValue ())
      {
         this .getBrowser () .addPointingDeviceSensor (this);

         delete this .push;
      }
      else
      {
         this .getBrowser () .removePointingDeviceSensor (this);

         if (this ._isActive .getValue ())
            this ._isActive = false;

         if (this ._isOver .getValue ())
            this ._isOver = false;

         this .push = Function .prototype;
      }
   },
   set_over__ (over, hit)
   {
      if (over === this ._isOver .getValue ())
         return;

      this ._isOver = over;

      if (over)
         this .getBrowser () .setDescription (this ._description);
   },
   set_active__ (active, hit)
   {
      if (active === this ._isActive .getValue ())
         return;

      this ._isActive = active
   },
   set_motion__ (hit)
   { },
   push (renderObject, sensors)
   {
      const sensor = PointingDeviceSensors .pop ();

      sensor .set (this,
                   renderObject .getModelViewMatrix  () .get (),
                   renderObject .getProjectionMatrix () .get (),
                   renderObject .getViewVolumes () .at (-1) .getViewport ());

      this .getBrowser () .addSensor (sensor);

      sensors .push (sensor);
   },
});

Object .defineProperties (X3DPointingDeviceSensorNode, Core_X3DNode .getStaticProperties ("X3DPointingDeviceSensorNode", "PointingDeviceSensor", 1));

const X3DPointingDeviceSensorNode_default_ = X3DPointingDeviceSensorNode;
;

/* harmony default export */ const PointingDeviceSensor_X3DPointingDeviceSensorNode = (Namespace/* default */.A .add ("X3DPointingDeviceSensorNode", X3DPointingDeviceSensorNode_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/X3DTouchSensorNode.js







function X3DTouchSensorNode (executionContext)
{
   PointingDeviceSensor_X3DPointingDeviceSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DTouchSensorNode);
}

Object .assign (Object .setPrototypeOf (X3DTouchSensorNode .prototype, PointingDeviceSensor_X3DPointingDeviceSensorNode .prototype),
{
   set_active__ (active, hit)
   {
      PointingDeviceSensor_X3DPointingDeviceSensorNode .prototype .set_active__ .call (this, active, hit);

      if (this ._enabled .getValue () && this ._isOver .getValue () && !active)
         this ._touchTime = this .getBrowser () .getCurrentTime ();
   },
   set_over__: (() =>
   {
      const
         invModelViewMatrix = new Numbers_Matrix4 (),
         texCoord           = new Numbers_Vector2 (),
         normal             = new Numbers_Vector3 (),
         point              = new Numbers_Vector3 ();

      return function (over, hit, modelViewMatrix, projectionMatrix, viewport)
      {
         PointingDeviceSensor_X3DPointingDeviceSensorNode .prototype .set_over__ .call (this, over, hit, modelViewMatrix, projectionMatrix, viewport);

         if (!this ._isOver .getValue ())
            return;

         invModelViewMatrix .assign (modelViewMatrix) .inverse ();

         texCoord .assign (hit .texCoord) .divide (hit .texCoord .w);
         normal   .assign (hit .normal);
         point    .assign (hit .point);

         this ._hitTexCoord_changed = texCoord;
         this ._hitNormal_changed   = modelViewMatrix .multMatrixDir (normal) .normalize ();
         this ._hitPoint_changed    = invModelViewMatrix .multVecMatrix (point);
      };
   })(),
});

Object .defineProperties (X3DTouchSensorNode, Core_X3DNode .getStaticProperties ("X3DTouchSensorNode", "PointingDeviceSensor", 1));

const X3DTouchSensorNode_default_ = X3DTouchSensorNode;
;

/* harmony default export */ const PointingDeviceSensor_X3DTouchSensorNode = (Namespace/* default */.A .add ("X3DTouchSensorNode", X3DTouchSensorNode_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/TouchSensor.js







function TouchSensor (executionContext)
{
   PointingDeviceSensor_X3DTouchSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TouchSensor);

   // Units

   this ._hitPoint_changed .setUnit ("length");
}

Object .setPrototypeOf (TouchSensor .prototype, PointingDeviceSensor_X3DTouchSensorNode .prototype);

Object .defineProperties (TouchSensor,
{
   ... Core_X3DNode .getStaticProperties ("TouchSensor", "PointingDeviceSensor", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",         new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "hitTexCoord_changed", new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "hitNormal_changed",   new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "hitPoint_changed",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isOver",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",            new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "touchTime",           new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const TouchSensor_default_ = TouchSensor;
;

/* harmony default export */ const PointingDeviceSensor_TouchSensor = (Namespace/* default */.A .add ("TouchSensor", TouchSensor_default_));
;// ./src/x_ite/Components/Networking/Anchor.js











function Anchor (executionContext)
{
   Grouping_X3DGroupingNode .call (this, executionContext);
   Networking_X3DUrlObject    .call (this, executionContext);

   this .addType (Base_X3DConstants .Anchor);

   // Private properties

   this .touchSensorNode = new PointingDeviceSensor_TouchSensor (executionContext);
   this .anchorSensors   = [ ];
}

Object .assign (Object .setPrototypeOf (Anchor .prototype, Grouping_X3DGroupingNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Grouping_X3DGroupingNode .prototype .initialize .call (this);
      Networking_X3DUrlObject    .prototype .initialize .call (this);

      this ._description .addFieldInterest (this .touchSensorNode ._description);
      this ._load        .addFieldInterest (this .touchSensorNode ._enabled);

      this .touchSensorNode ._description = this ._description;
      this .touchSensorNode ._enabled     = this ._load;
      this .touchSensorNode .setup ();

      // Modify set_active__ to get immediate response to user action (click event),
      // otherwise links are not opened in this window.

      this .touchSensorNode .set_active__ = (active, hit) =>
      {
         PointingDeviceSensor_TouchSensor .prototype .set_active__ .call (this .touchSensorNode, active, hit);

         if (this .touchSensorNode ._isOver .getValue () && !active)
            this .requestImmediateLoad () .catch (Function .prototype);
      };
   },
   set_load__ ()
   { },
   set_url__ ()
   { },
   requestImmediateLoad (cache = true)
   {
      this .setCache (cache);
      this .setLoadState (Base_X3DConstants .IN_PROGRESS_STATE, false);

      return new Promise ((resolve, reject) =>
      {
         new InputOutput_FileLoader (this) .createX3DFromURL (this ._url, this ._parameter,
         (scene) =>
         {
            if (scene)
            {
               this .getBrowser () .replaceWorld (scene);
               this .setLoadState (Base_X3DConstants .COMPLETE_STATE, false);
               resolve ();
            }
            else
            {
               this .setLoadState (Base_X3DConstants .FAILED_STATE, false);
               reject ();
            }
         },
         (viewpointName) =>
         {
            this .getBrowser () .changeViewpoint (viewpointName);
            this .setLoadState (Base_X3DConstants .COMPLETE_STATE, false);
            resolve ();
         },
         (url, target) =>
         {
            if (target)
               window .open (url, target);
            else
               location = url;

            this .setLoadState (Base_X3DConstants .COMPLETE_STATE, false);
            resolve ();
         });
      });
   },
   requestUnload ()
   { },
   traverse (type, renderObject)
   {
      if (type === Rendering_TraverseType .POINTER)
      {
         const sensors = this .anchorSensors;

         sensors .length = 0;

         this .touchSensorNode .push (renderObject, sensors);

         if (sensors .length)
         {
            renderObject .getSensors () .push (sensors);

            Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);

            renderObject .getSensors () .pop ();
         }
         else
         {
            Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);
         }
      }
      else
      {
         Grouping_X3DGroupingNode .prototype .traverse .call (this, type, renderObject);
      }
   },
   dispose ()
   {
      Networking_X3DUrlObject    .prototype .dispose .call (this);
      Grouping_X3DGroupingNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (Anchor,
{
   ... Core_X3DNode .getStaticProperties ("Anchor", "Networking", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "parameter",            new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",             new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",           new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "addChildren",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "removeChildren",       new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",             new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Anchor_default_ = Anchor;
;

/* harmony default export */ const Networking_Anchor = (Namespace/* default */.A .add ("Anchor", Anchor_default_));
;// ./src/x_ite/Components/Networking/Inline.js












function Inline (executionContext)
{
   Core_X3DChildNode     .call (this, executionContext);
   Networking_X3DUrlObject     .call (this, executionContext);
   Grouping_X3DBoundedObject .call (this, executionContext);

   this .addType (Base_X3DConstants .Inline);

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.3)
      this ._global = true;

   // Private properties

   this .scene        = this .getBrowser () .getDefaultScene ();
   this .groupNode    = new Grouping_Group (executionContext);
   this .localLights  = [ ];
   this .localShadows = false;
}

Object .assign (Object .setPrototypeOf (Inline .prototype, Core_X3DChildNode .prototype),
   Networking_X3DUrlObject .prototype,
   Grouping_X3DBoundedObject .prototype,
{
   initialize ()
   {
      Core_X3DChildNode     .prototype .initialize .call (this);
      Networking_X3DUrlObject     .prototype .initialize .call (this);
      Grouping_X3DBoundedObject .prototype .initialize .call (this);

      this .groupNode .setPrivate (true);
      this .groupNode .setup ();

      this .connectChildNode (this .groupNode);
      this .requestImmediateLoad () .catch (Function .prototype);
   },
   getBBox (bbox, shadows)
   {
      if (this .isDefaultBBoxSize ())
         return this .groupNode .getBBox (bbox, shadows);

      return bbox .set (this ._bboxSize .getValue (), this ._bboxCenter .getValue ());
   },
   getShapes (shapes, modelMatrix)
   {
      return this .groupNode .getShapes (shapes, modelMatrix);
   },
   unloadData ()
   {
      this .fileLoader ?.abort ();
      this .setInternalScene (this .getBrowser () .getDefaultScene ());
   },
   loadData ()
   {
      this .fileLoader ?.abort ();
      this .fileLoader = new InputOutput_FileLoader (this) .createX3DFromURL (this ._url, null, this .setInternalScene .bind (this));
   },
   setInternalScene (scene)
   {
      if (this .scene !== this .getBrowser () .getDefaultScene ())
         this .scene .dispose ();

      // Set new scene.

      this .scene = scene ?? this .getBrowser () .getDefaultScene ();

      if (scene)
      {
         this .scene .setExecutionContext (this .getExecutionContext ());
         this .scene .setLive (true);
         this .scene .rootNodes .addFieldInterest (this .groupNode ._children);

         this .groupNode ._children = scene .rootNodes;

         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
      }
      else
      {
         this .groupNode ._children .length = 0;

         this .setLoadState (Base_X3DConstants .FAILED_STATE);
      }

      this .getBrowser () .addBrowserEvent ();
   },
   getInternalScene ()
   {
      ///  Returns the internal X3DScene of this Inline node, that is loaded from the url given.
      ///  If the load field was false an empty scene is returned.  This empty scene is the same for all Inline
      ///  nodes (due to performance reasons).

      return this .scene;
   },
   traverse (type, renderObject)
   {
      switch (type)
      {
         case Rendering_TraverseType .PICKING:
         {
            const
               browser          = this .getBrowser (),
               pickingHierarchy = browser .getPickingHierarchy ();

            pickingHierarchy .push (this);

            this .groupNode .traverse (type, renderObject);

            pickingHierarchy .pop ();
            return;
         }
         case Rendering_TraverseType .DISPLAY:
         {
            if (this ._global .getValue ())
            {
               this .groupNode .traverse (type, renderObject);
            }
            else
            {
               const
                  globalLights    = renderObject .getGlobalLights (),
                  globalShadows   = renderObject .getGlobalShadows (),
                  globalsBegin    = globalLights .length,
                  shadowsBegin    = globalShadows .length,
                  localLights     = this .localLights,
                  numLocalObjects = localLights .length,
                  lightsKeys      = localLights .map (c => c .lightNode .getLightKey ());

               if (numLocalObjects)
               {
                  renderObject .getLocalObjects () .push (... localLights);
                  renderObject .pushLocalShadows (this .localShadows);
                  renderObject .getLocalObjectsKeys () .push (... lightsKeys);
               }

               this .groupNode .traverse (type, renderObject);

               if (numLocalObjects)
               {
                  if (renderObject .isIndependent ())
                  {
                     const browser = this .getBrowser ();

                     for (let i = 0; i < numLocalObjects; ++ i)
                        browser .getLocalObjects () .add (renderObject .getLocalObjects () .pop ());
                  }
                  else
                  {
                     for (let i = 0; i < numLocalObjects; ++ i)
                        renderObject .getLocalObjects () .pop ();
                  }

                  renderObject .popLocalShadows ();
                  renderObject .getLocalObjectsKeys () .length -= lightsKeys .length;
               }

               const numGlobalLights = globalLights .length - globalsBegin;

               for (let i = 0; i < numGlobalLights; ++ i)
               {
                  const globalLight = globalLights [globalsBegin + i];

                  globalLight .groupNode = this .groupNode;
                  globalLight .global    = false;

                  localLights [i] = globalLight;
               }

               localLights .length = numGlobalLights;
               this .localShadows  = globalShadows .at (-1);

               globalLights  .length = globalsBegin;
               globalShadows .length = shadowsBegin;
            }

            return;
         }
         default:
         {
            this .groupNode .traverse (type, renderObject);
            return;
         }
      }
   },
   dispose ()
   {
      // Remove all imported nodes with this Inline node.

      const executionContext = this .getExecutionContext ();

      for (const importedNode of Array .from (executionContext .getImportedNodes ()))
      {
         if (importedNode .getInlineNode () === this)
            executionContext .removeImportedNode (importedNode .getImportedName ());
      }

      Grouping_X3DBoundedObject .prototype .dispose .call (this);
      Networking_X3DUrlObject     .prototype .dispose .call (this);
      Core_X3DChildNode     .prototype .dispose .call (this);
   },
});

Object .defineProperties (Inline,
{
   ... Core_X3DNode .getStaticProperties ("Inline", "Networking", 2, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "global",               new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "visible",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "bboxDisplay",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxSize",             new x_ite_Fields .SFVec3f (-1, -1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "bboxCenter",           new x_ite_Fields .SFVec3f ()),
      ]),
      enumerable: true,
   },
});

const Inline_default_ = Inline;
;

/* harmony default export */ const Networking_Inline = (Namespace/* default */.A .add ("Inline", Inline_default_));
;// ./src/x_ite/Components/Networking/InlineGeometry.js












/**
 * THIS NODE IS STILL EXPERIMENTAL.
 */

function InlineGeometry (executionContext)
{
   Rendering_X3DLineGeometryNode .call (this, executionContext);
   Networking_X3DUrlObject        .call (this, executionContext);

   this .addType (Base_X3DConstants .InlineGeometry);
}

Object .assign (Object .setPrototypeOf (InlineGeometry .prototype, Rendering_X3DGeometryNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Rendering_X3DGeometryNode .prototype .initialize .call (this);
      Networking_X3DUrlObject    .prototype .initialize .call (this);

      this ._solid  .addInterest ("set_solid__",  this);
      this ._smooth .addInterest ("set_smooth__", this);

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_solid__ ()
   {
      if (!this .geometryNode)
         return;

      if (this .geometryNode .getGeometryType () < 2)
         return;

      this .geometryNode ._solid = this ._solid;
   },
   set_smooth__ ()
   {
      if (!this .geometryNode)
         return;

      if (this .geometryNode .getGeometryType () < 2)
         return;

      const smooth = this ._smooth .getValue ();

      if (this .geometryNode ._creaseAngle)
      {
         const creaseAngle = smooth ? Math .PI : 0;

         if (this .geometryNode ._creaseAngle .equals (creaseAngle))
            return;

         this .geometryNode ._creaseAngle = creaseAngle;
      }
      else if (this .geometryNode ._normal && !this .geometryNode ._normal .getValue ())
      {
         if (this .geometryNode ._normalPerVertex .equals (smooth))
            return;

         this .geometryNode ._normalPerVertex = smooth;
      }
   },
   unloadData ()
   {
      this .fileLoader ?.abort ();
      this .setInternalScene (null);
   },
   loadData ()
   {
      const cache = this .getBrowser () .getBrowserOption ("Cache");

      this .fileLoader ?.abort ();

      this .fileLoader = new InputOutput_FileLoader (this, { cacheScene: cache })
         .createX3DFromURL (this ._url, null, this .setInternalScene .bind (this));
   },
   setInternalScene (scene)
   {
      // Remove old scene.

      if (!this .scene ?.cache)
         this .scene ?.dispose ();

      // Set new scene.

      this .scene = scene;

      if (scene)
      {
         const
            browser = this .getBrowser (),
            hash    = new URL (scene .getWorldURL ()) .hash .substring (1);

         this .geometryNode = hash
            ? Base_X3DCast (Base_X3DConstants .X3DGeometryNode, scene .getNamedNode (hash))
            : this .getGeometryFromArray (scene .rootNodes);

         if (!this .geometryNode)
            throw new Error ("No X3DGeometryNode found.");

         this .scene .setExecutionContext (scene .cache ? browser .getDefaultScene () : this .getExecutionContext ());
         this .scene .setLive (true);

         this .set_solid__ ();
         this .set_smooth__ ();

         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);
      }
      else
      {
         this .geometryNode = null;

         this .setLoadState (Base_X3DConstants .FAILED_STATE);
      }

      Base_X3DChildObject .prototype .addEvent .call (this);
   },
   getInternalScene ()
   {
      ///  Returns the internal X3DScene of this InlineGeometry node, that is loaded from the url given.
      ///  If the load field was false, null is returned.

      return this .scene;
   },
   getInnerNode ()
   {
      if (this .geometryNode)
         return this .geometryNode;

      throw new Error ("Geometry node not available.");
   },
   getGeometryFromArray (nodes)
   {
      for (const node of nodes)
      {
         const geometryNode = this .getGeometryFromNode (node ?.getValue ())

         if (geometryNode)
            return geometryNode;
      }
   },
   getGeometryFromNode (node)
   {
      if (!node)
         return;

      const geometryNode = Base_X3DCast (Base_X3DConstants .X3DGeometryNode, node);

      if (geometryNode)
         return geometryNode;

      for (const field of node .getFields ())
      {
         switch (field .getType ())
         {
            case Base_X3DConstants .SFNode:
            {
               const geometryNode = this .getGeometryFromNode (field .getValue ());

               if (geometryNode)
                  return geometryNode;

               break;
            }
            case Base_X3DConstants .MFNode:
            {
               const geometryNode = this .getGeometryFromArray (field);

               if (geometryNode)
                  return geometryNode;

               break;
            }
         }
      }
   },
   build ()
   { },
   dispose ()
   {
      Networking_X3DUrlObject    .prototype .dispose .call (this);
      Rendering_X3DGeometryNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (InlineGeometry,
{
   ... Core_X3DNode .getStaticProperties ("InlineGeometry", "Networking", 4, "geometry", "4.1"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "solid",                new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "smooth",               new x_ite_Fields .SFBool (true)),
      ]),
      enumerable: true,
   },
});

const InlineGeometry_default_ = InlineGeometry;
;

/* harmony default export */ const Networking_InlineGeometry = (Namespace/* default */.A .add ("InlineGeometry", InlineGeometry_default_));
;// ./src/x_ite/Components/Networking/X3DUrlOutputObject.js



function X3DUrlOutputObject (executionContext)
{
   this .addType (Base_X3DConstants .X3DUrlOutputObject);
}

Object .assign (X3DUrlOutputObject .prototype,
{
   initialize ()
   { },
   dispose ()
   { },
});

Object .defineProperties (X3DUrlOutputObject, Core_X3DNode .getStaticProperties ("X3DUrlOutputObject", "Networking", 4));

const X3DUrlOutputObject_default_ = X3DUrlOutputObject;
;

/* harmony default export */ const Networking_X3DUrlOutputObject = (Namespace/* default */.A .add ("X3DUrlOutputObject", X3DUrlOutputObject_default_));
;// ./src/x_ite/Components/NetworkingComponent.js








const NetworkingComponent_default_ = {
   name: "Networking",
   concreteNodes:
   [
      Networking_Anchor,
      Networking_Inline,
      Networking_InlineGeometry,
      Networking_LoadSensor,
   ],
   abstractNodes:
   [
      Networking_X3DNetworkSensorNode,
      Networking_X3DUrlObject,
      Networking_X3DUrlOutputObject,
   ],
};
;

/* harmony default export */ const NetworkingComponent = (Namespace/* default */.A .add ("NetworkingComponent", NetworkingComponent_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/X3DDragSensorNode.js




function X3DDragSensorNode (executionContext)
{
   PointingDeviceSensor_X3DPointingDeviceSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DDragSensorNode);

   // Units

   this ._trackPoint_changed .setUnit ("length");
}

Object .setPrototypeOf (X3DDragSensorNode .prototype, PointingDeviceSensor_X3DPointingDeviceSensorNode .prototype);

Object .defineProperties (X3DDragSensorNode, Core_X3DNode .getStaticProperties ("X3DDragSensorNode", "PointingDeviceSensor", 1));

const X3DDragSensorNode_default_ = X3DDragSensorNode;
;

/* harmony default export */ const PointingDeviceSensor_X3DDragSensorNode = (Namespace/* default */.A .add ("X3DDragSensorNode", X3DDragSensorNode_default_));
;// ./src/standard/Math/Geometry/Cylinder3.js





function Cylinder3 (axis = new Geometry_Line3 (), radius = 1)
{
   this .axis = new Geometry_Line3 ();

   this .set (axis, radius);
}

Object .assign (Cylinder3 .prototype,
{
   copy ()
   {
      const copy = Object .create (Cylinder3 .prototype);

      copy .radius = this .radius;
      copy .axis   = this .axis .copy ();

      return copy;
   },
   assign (cylinder)
   {
      this .radius = cylinder .radius;
      this .axis .assign (cylinder .axis);

      return this;
   },
   equals (cylinder)
   {
      return this .radius === cylinder .radius && this .axis .equals (cylinder .axis);
   },
   set (axis = new Geometry_Line3 (), radius = 1)
   {
      this .axis .assign (axis);
      this .radius = radius;
   },
   intersectsLine (line, enter, exit)
   {
      ////////////////////////////////////////////////////////////////////////
      //
      // Description:
      //  Intersect given line with this cylinder, returning the
      //  results in enter and exit. Returns TRUE if there was an
      //  intersection (and results are valid).
      //
      // Taken from Inventor SbCylinder.c++

      // The intersection will actually be done on a radius 1 cylinder
      // aligned with the y axis, so we transform the line into that
      // space, then intersect, then transform the results back.

      // rotation to y axis
      const
         rotToYAxis = Numbers_Rotation4 .fromVectors (this .axis .direction, new Numbers_Vector3 (0, 1, 0)),
         mtxToYAxis = Numbers_Matrix4 .fromRotation (rotToYAxis);

      // scale to unit space
      const
         scaleFactor    = 1 / this .radius,
         toUnitCylSpace = new Numbers_Matrix4 ();

      toUnitCylSpace .scale (new Numbers_Vector3 (scaleFactor));
      toUnitCylSpace .multLeft (mtxToYAxis);

      // find the given line un-translated
      const
         point             = line .point .copy () .subtract (this .axis .point),
         noTranslationLine = new Geometry_Line3 (point, line .direction);

      // find the un-translated line in unit cylinder's space
      const cylLine = noTranslationLine .multLineMatrix (toUnitCylSpace);

      // find the intersection on the unit cylinder
      const intersected = this .unitCylinderIntersectsLine (cylLine, enter, exit);

      if (!intersected)
         return false;

      // transform back to original space
      const fromUnitCylSpace = toUnitCylSpace .inverse ();

      fromUnitCylSpace .multVecMatrix (enter);
      enter .add (this .axis .point);

      fromUnitCylSpace .multVecMatrix (exit);
      exit .add (this .axis .point);

      return true;
   },
   unitCylinderIntersectsLine (line, enter, exit)
   {
      let t0, t1;

      const
         pos = line .point,
         dir = line .direction;

      const
         A = dir [0] * dir [0] + dir [2] * dir [2],
         B = 2 * (pos [0] * dir [0] + pos [2] * dir [2]),
         C = pos [0] * pos [0] + pos [2] * pos [2] - 1;

      // discriminant = B^2 - 4AC
      const discr = B * B - 4 * A * C;

      // if discriminant is negative, no intersection
      if (discr < 0)
         return false;

         const sqroot = Math .sqrt (discr);

      // magic to stabilize the answer
      if (B > 0)
      {
         t0 = -(2 * C) / (sqroot + B);
         t1 = -(sqroot + B) / (2 * A);
      }
      else
      {
         t0 = (2 * C) / (sqroot - B);
         t1 = (sqroot - B) / (2 * A);
      }

      enter .assign (dir) .multiply (t0) .add (pos);
      exit  .assign (dir) .multiply (t1) .add (pos);

      return true;
   },
   toString ()
   {
      return `${this .axis}, ${this .radius}`;
   },
});

const Cylinder3_default_ = Cylinder3;
;

/* harmony default export */ const Geometry_Cylinder3 = (Namespace/* default */.A .add ("Cylinder3", Cylinder3_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/CylinderSensor.js














function CylinderSensor (executionContext)
{
   PointingDeviceSensor_X3DDragSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .CylinderSensor);

   // Units

   this ._diskAngle .setUnit ("angle");
   this ._minAngle  .setUnit ("angle");
   this ._maxAngle  .setUnit ("angle");
   this ._offset    .setUnit ("angle");
}

Object .assign (Object .setPrototypeOf (CylinderSensor .prototype, PointingDeviceSensor_X3DDragSensorNode .prototype),
{
   initialize ()
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .initialize .call (this);

      this ._axisRotation .addInterest ("set_fields__", this);
      this ._diskAngle    .addInterest ("set_fields__", this);

      this .modelViewMatrix    = new Numbers_Matrix4 ();
      this .invModelViewMatrix = new Numbers_Matrix4 ();

      this .cylinder    = new Geometry_Cylinder3 (new Geometry_Line3 (), 0);
      this .disk        = false;
      this .yPlane      = null;
      this .zPlane      = null;
      this .sxPlane     = null;
      this .szNormal    = null;
      this .behind      = false;
      this .fromVector  = new Numbers_Vector3 ();
      this .startOffset = new Numbers_Rotation4 ();
   },
   isBehind (hitRay, hitPoint)
   {
      const
         enter = new Numbers_Vector3 (),
         exit  = new Numbers_Vector3 ();

      this .cylinder .intersectsLine (hitRay, enter, exit);

      return hitPoint .distance (enter) > hitPoint .distance (exit);
   },
   getTrackPoint (hitRay, trackPoint)
   {
      const zPoint = new Numbers_Vector3 ();

      this .zPlane .intersectsLine (hitRay, zPoint);

      const
         axisPoint = zPoint .copy () .add (this .cylinder .axis .getPerpendicularVectorToPoint (zPoint, new Numbers_Vector3 ())),
         distance  = this .sxPlane .getDistanceToPoint (zPoint) / this .cylinder .radius,
         section   = Math .floor ((distance + 1) / 2);

      // Use asin on the cylinder and outside linear angle.
      const
         sinp  = Math_Algorithm .interval (distance, -1, 1),
         phi   = section === 0 ? Math .asin (sinp) : sinp * Math .PI / 2,
         angle = phi + section * Math .PI;

      const rotation = new Numbers_Rotation4 (... this .cylinder .axis .direction, angle);

      rotation .multVecRot (trackPoint .assign (this .szNormal) .multiply (this .cylinder .radius));
      trackPoint .add (axisPoint);
   },
   getAngle (rotation)
   {
      if (rotation .getAxis (new Numbers_Vector3 ()) .dot (this .cylinder .axis .direction) > 0)
         return rotation .angle;
      else
         return -rotation .angle;
   },
   set_fields__ ()
   {
      if (!this ._isActive .getValue ())
         return;

      this .activate (this .activateHit);
      this .set_motion__ (this .motionHit ?? this .activateHit);
   },
   set_active__ (active, hit, modelViewMatrix, projectionMatrix, viewport)
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .set_active__ .call (this, active, hit, modelViewMatrix, projectionMatrix, viewport);

      if (this ._isActive .getValue ())
      {
         this .activateHit = hit .copy ();
         this .motionHit   = null;

         this .modelViewMatrix    .assign (modelViewMatrix);
         this .invModelViewMatrix .assign (modelViewMatrix) .inverse ();

         this .activate (hit);
      }
      else
      {
         if (this ._autoOffset .getValue ())
            this ._offset = this .getAngle (this ._rotation_changed .getValue ());
      }
   },
   activate (hit)
   {
      const
         hitRay   = hit .ray .copy () .multLineMatrix (this .invModelViewMatrix),
         hitPoint = this .invModelViewMatrix .multVecMatrix (hit .point .copy ());

      const
         yAxis      = this ._axisRotation .getValue () .multVecRot (new Numbers_Vector3 (0, 1, 0)),
         cameraBack = this .invModelViewMatrix .multDirMatrix (new Numbers_Vector3 (0, 0, 1)) .normalize ();

      const
         axis   = new Geometry_Line3 (Numbers_Vector3 .ZERO, yAxis),
         radius = axis .getPerpendicularVectorToPoint (hitPoint, new Numbers_Vector3 ()) .norm ();

      this .cylinder = new Geometry_Cylinder3 (axis, radius);
      this .disk     = Math .abs (cameraBack .dot (yAxis)) > Math .cos (this ._diskAngle .getValue ());
      this .behind   = this .isBehind (hitRay, hitPoint);
      this .yPlane   = new Geometry_Plane3 (hitPoint, yAxis);             // Sensor aligned y-plane
      this .zPlane   = new Geometry_Plane3 (hitPoint, cameraBack);        // Screen aligned z-plane

      // Compute normal like in Billboard with yAxis as axis of rotation.
      const
         billboardToViewer = this .invModelViewMatrix .origin,
         sxNormal          = yAxis .copy () .cross (billboardToViewer) .normalize ();

      this .sxPlane  = new Geometry_Plane3 (Numbers_Vector3 .ZERO, sxNormal);   // Billboarded special x-plane made parallel to sensors axis.
      this .szNormal = sxNormal .copy () .cross (yAxis) .normalize (); // Billboarded special z-normal made parallel to sensors axis.

      const trackPoint = new Numbers_Vector3 ();

      if (this .disk)
         this .yPlane .intersectsLine (hitRay, trackPoint);
      else
         this .getTrackPoint (hitRay, trackPoint);

      this .fromVector  = this .cylinder .axis .getPerpendicularVectorToPoint (trackPoint, new Numbers_Vector3 ()) .negate ();
      this .startOffset = new Numbers_Rotation4 (... yAxis, this ._offset .getValue ());

      this ._trackPoint_changed = trackPoint;
      this ._rotation_changed   = this .startOffset;

      // For min/max angle.

      this .angle       = this ._offset .getValue ();
      this .startVector = this ._rotation_changed .getValue () .multVecRot (this ._axisRotation .getValue () .multVecRot (new Numbers_Vector3 (0, 0, 1)));
   },
   set_motion__ (hit)
   {
      this .motionHit = hit .copy ();

      const
         hitRay     = hit .ray .copy () .multLineMatrix (this .invModelViewMatrix),
         trackPoint = new Numbers_Vector3 ();

      if (this .disk)
         this .yPlane .intersectsLine (hitRay, trackPoint);
      else
         this .getTrackPoint (hitRay, trackPoint);

      this ._trackPoint_changed = trackPoint;

      const
         toVector = this .cylinder .axis .getPerpendicularVectorToPoint (trackPoint, new Numbers_Vector3 ()) .negate (),
         rotation = Numbers_Rotation4 .fromVectors (this .fromVector, toVector);

      if (this .disk)
      {
         // The trackpoint can swap behind the viewpoint if viewpoint is a Viewpoint node
         // as the viewing volume is not a cube where the picking ray goes straight up.
         // This phenomenon is very clear on the viewport corners.

         const trackPoint_ = this .modelViewMatrix .multVecMatrix (trackPoint .copy ());

         if (trackPoint_ .z > 0)
            rotation .multRight (new Numbers_Rotation4 (... this .yPlane .normal, Math .PI));
      }
      else
      {
         if (this .behind)
            rotation .inverse ();
      }

      rotation .multLeft (this .startOffset);

      if (this ._minAngle .getValue () > this ._maxAngle .getValue ())
      {
         this ._rotation_changed = rotation;
      }
      else
      {
         const
            endVector     = rotation .multVecRot (this ._axisRotation .getValue () .multVecRot (new Numbers_Vector3 (0, 0, 1))),
            deltaRotation = Numbers_Rotation4 .fromVectors (this .startVector, endVector),
            axis          = this ._axisRotation .getValue () .multVecRot (new Numbers_Vector3 (0, 1, 0)),
            sign          = axis .dot (deltaRotation .getAxis (new Numbers_Vector3 ())) > 0 ? 1 : -1,
            min           = this ._minAngle .getValue (),
            max           = this ._maxAngle .getValue ();

         this .angle += sign * deltaRotation .angle;

         this .startVector .assign (endVector);

         //console .log (this .angle, min, max);

         if (this .angle < min)
            rotation .setAxisAngle (this .cylinder .axis .direction, min);
         else if (this .angle > max)
            rotation .setAxisAngle (this .cylinder .axis .direction, max);
         else
            rotation .setAxisAngle (this .cylinder .axis .direction, this .angle);

         if (! this ._rotation_changed .getValue () .equals (rotation))
            this ._rotation_changed = rotation;
      }
   },
});

Object .defineProperties (CylinderSensor,
{
   ... Core_X3DNode .getStaticProperties ("CylinderSensor", "PointingDeviceSensor", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",        new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "axisRotation",       new x_ite_Fields .SFRotation (0, 0, 1, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diskAngle",          new x_ite_Fields .SFFloat (0.26179167)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "minAngle",           new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "maxAngle",           new x_ite_Fields .SFFloat (-1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "offset",             new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoOffset",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "trackPoint_changed", new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "rotation_changed",   new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isOver",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",           new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const CylinderSensor_default_ = CylinderSensor;
;

/* harmony default export */ const PointingDeviceSensor_CylinderSensor = (Namespace/* default */.A .add ("CylinderSensor", CylinderSensor_default_));
;// ./src/standard/Math/Geometry/Line2.js


function Line2 (point = Numbers_Vector2 .ZERO, direction = Numbers_Vector2 .Y_AXIS)
{
   this .point     = new Numbers_Vector2 ();
   this .direction = new Numbers_Vector2 ();

   this .set (point, direction);
}

Object .assign (Line2 .prototype,
{
   copy ()
   {
      const copy = Object .create (Line2 .prototype);

      copy .point     = this .point .copy ();
      copy .direction = this .direction .copy ();

      return copy;
   },
   assign (line)
   {
      this .point     .assign (line .point);
      this .direction .assign (line .direction);

      return this;
   },
   equals (line)
   {
      return this .point .equals (line .point) && this .direction .equals (line .direction);
   },
   set (point = Numbers_Vector2 .ZERO, direction = Numbers_Vector2 .Y_AXIS)
   {
      this .point     .assign (point);
      this .direction .assign (direction);
      return this;
   },
   setPoints (point1, point2)
   {
      this .point .assign (point1);
      this .direction .assign (point2) .subtract (point1) .normalize ();
      return this;
   },
   multMatrixLine (matrix)
   {
      matrix .multMatrixVec (this .point);
      matrix .multMatrixDir (this .direction) .normalize ();
      return this;
   },
   multLineMatrix (matrix)
   {
      matrix .multVecMatrix (this .point);
      matrix .multDirMatrix (this .direction) .normalize ();
      return this;
   },
   getClosestPointToPoint (point, result = new Numbers_Vector2 ())
   {
      const
         r = result .assign (point) .subtract (this .point),
         d = r .dot (this .direction);

      return result .assign (this .direction) .multiply (d) .add (this .point);
   },
   getPerpendicularVectorToPoint: (() =>
   {
      const t = new Numbers_Vector2 ();

      return function (point, result = new Numbers_Vector2 ())
      {
         result .assign (this .point) .subtract (point);

         return result .subtract (t .assign (this .direction) .multiply (result .dot (this .direction)));
      };
   })(),
   intersectsLine: (() =>
   {
      const u = new Numbers_Vector2 ();

      return function (line, point)
      {
         const
            { point: p1, direction: d1 } = this,
            { point: p2, direction: d2 } = line;

         const theta = d1 .dot (d2); // angle between both lines

         if (Math .abs (theta) >= 1)
            return false; // lines are parallel

         u .assign (p2) .subtract (p1);

         const t = (u .dot (d1) - theta * u .dot (d2)) / (1 - theta * theta);

         point .assign (d1) .multiply (t) .add (p1);

         return true;
      };
   })(),
   toString ()
   {
      return `${this .point}, ${this .direction}`;
   },
});

Object .assign (Line2,
{
   fromPoints (point1, point2)
   {
      const line = Object .create (Line2 .prototype);
      line .point     = point1 .copy ();
      line .direction = point2 .copy () .subtract (point1) .normalize ();
      return line;
   },
});

const Line2_default_ = Line2;
;

/* harmony default export */ const Geometry_Line2 = (Namespace/* default */.A .add ("Line2", Line2_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/PlaneSensor.js
















const
   screenLine     = new Geometry_Line2 (),
   trackPoint1    = new Numbers_Vector2 (),
   trackPointLine = new Geometry_Line3 ();

function PlaneSensor (executionContext)
{
   PointingDeviceSensor_X3DDragSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PlaneSensor);

   // Units

   this ._offset              .setUnit ("length");
   this ._minPosition         .setUnit ("length");
   this ._maxPosition         .setUnit ("length");
   this ._translation_changed .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (PlaneSensor .prototype, PointingDeviceSensor_X3DDragSensorNode .prototype),
{
   initialize ()
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .initialize .call (this);

      this ._axisRotation .addInterest ("set_fields__", this);
      this ._minPosition  .addInterest ("set_fields__", this);
      this ._maxPosition  .addInterest ("set_fields__", this);

      this .modelViewMatrix    = new Numbers_Matrix4 ();
      this .invModelViewMatrix = new Numbers_Matrix4 ();
      this .projectionMatrix   = new Numbers_Matrix4 ();
      this .viewport           = new Numbers_Vector4 ();

      this .planeSensor = true;
      this .plane       = null;
      this .line        = null;
      this .startOffset = new Numbers_Vector3 ();
      this .startPoint  = new Numbers_Vector3 ();
   },
   getLineTrackPoint (pointer, line, trackPoint)
   {
      Geometry_ViewVolume .projectLine (line, this .modelViewMatrix, this .projectionMatrix, this .viewport, screenLine);
      screenLine .getClosestPointToPoint (pointer, trackPoint1);
      Geometry_ViewVolume .unProjectRay (trackPoint1 .x, trackPoint1 .y, this .modelViewMatrix, this .projectionMatrix, this .viewport, trackPointLine);

      return line .getClosestPointToLine (trackPointLine, trackPoint);
   },
   set_fields__ ()
   {
      if (!this ._isActive .getValue ())
         return;

      this .activate (this .activateHit);
      this .set_motion__ (this .motionHit ?? this .activateHit);
   },
   set_active__ (active, hit, modelViewMatrix, projectionMatrix, viewport)
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .set_active__ .call (this, active, hit, modelViewMatrix, projectionMatrix, viewport);

      if (this ._isActive .getValue ())
      {
         this .activateHit = hit .copy ();
         this .motionHit   = null;

         this .modelViewMatrix    .assign (modelViewMatrix);
         this .projectionMatrix   .assign (projectionMatrix);
         this .viewport           .assign (viewport);
         this .invModelViewMatrix .assign (modelViewMatrix) .inverse ();

         this .activate (hit);
      }
      else
      {
         if (this ._autoOffset .getValue ())
            this ._offset = this ._translation_changed;
      }
   },
   activate (hit)
   {
      const
         hitRay   = hit .ray .copy () .multLineMatrix (this .invModelViewMatrix),
         hitPoint = this .invModelViewMatrix .multVecMatrix (hit .point .copy ());

      const axisRotation = this ._axisRotation .getValue ();

      if (this ._minPosition .x === this ._maxPosition .x)
      {
         this .planeSensor = false;

         const direction = axisRotation .multVecRot (new Numbers_Vector3 (0, Math .abs (this ._maxPosition .y - this ._minPosition .y), 0));

         this .line = new Geometry_Line3 (hitPoint, direction .normalize ());
      }
      else if (this ._minPosition .y === this ._maxPosition .y)
      {
         this .planeSensor = false;

         const direction = axisRotation .multVecRot (new Numbers_Vector3 (Math .abs (this ._maxPosition .x - this ._minPosition .x), 0, 0));

         this .line = new Geometry_Line3 (hitPoint, direction .normalize ());
      }
      else
      {
         this .planeSensor = true;
         this .plane       = new Geometry_Plane3 (hitPoint, axisRotation .multVecRot (new Numbers_Vector3 (0, 0, 1)));
      }

      if (this .planeSensor)
      {
         if (this .plane .intersectsLine (hitRay, this .startPoint))
         {
            this .trackStart (this .startPoint);
         }

         // new Plane3 (Vector3 .ZERO, this .plane .normal) .intersectsLine (hitRay, trackPoint);
      }
      else
      {
         if (this .getLineTrackPoint (hit .pointer, this .line, this .startPoint))
         {
            const trackPoint = new Numbers_Vector3 ();

            try
            {
               this .getLineTrackPoint (hit .pointer, new Geometry_Line3 (this .line .direction, this .line .direction), trackPoint);
            }
            catch
            {
               trackPoint .assign (this .startPoint);
            }

            this .trackStart (trackPoint);
         }
      }
   },
   trackStart (trackPoint)
   {
      this .startOffset .assign (this ._offset .getValue ());

      this ._trackPoint_changed  = trackPoint;
      this ._translation_changed = this ._offset .getValue ();
   },
   set_motion__ (hit)
   {
      try
      {
         this .motionHit = hit .copy ();

         if (this .planeSensor)
         {
            const
               hitRay   = hit .ray .copy () .multLineMatrix (this .invModelViewMatrix),
               endPoint = new Numbers_Vector3 ();

            if (this .plane .intersectsLine (hitRay, endPoint))
               this .track (endPoint, endPoint .copy ());
            else
               throw new Error ("Plane and line are parallel.");
         }
         else
         {
            const
               endPoint   = new Numbers_Vector3 (),
               trackPoint = new Numbers_Vector3 ();

            if (this .getLineTrackPoint (hit .pointer, this .line, endPoint))
            {
               try
               {
                  this .getLineTrackPoint (hit .pointer, new Geometry_Line3 (Numbers_Vector3 .ZERO, this .line .direction), trackPoint);
               }
               catch
               {
                  trackPoint .assign (endPoint);
               }

               this .track (endPoint, trackPoint);
            }
            else
            {
               throw new Error ("Lines are parallel.");
            }
         }
      }
      catch
      {
         this ._trackPoint_changed  .addEvent ();
         this ._translation_changed .addEvent ();
      }
   },
   track (endPoint, trackPoint)
   {
      const
         axisRotation = this ._axisRotation .getValue (),
         translation  = axisRotation .copy () .inverse () .multVecRot (endPoint .add (this .startOffset) .subtract (this .startPoint));

      // X component

      if (!(this ._minPosition .x > this ._maxPosition .x))
         translation .x = Math_Algorithm .clamp (translation .x, this ._minPosition .x, this ._maxPosition .x);

      // Y component

      if (!(this ._minPosition .y > this ._maxPosition .y))
         translation .y = Math_Algorithm .clamp (translation .y, this ._minPosition .y, this ._maxPosition .y);

      axisRotation .multVecRot (translation);

      if (!this ._trackPoint_changed .getValue () .equals (trackPoint))
         this ._trackPoint_changed = trackPoint;

      if (!this ._translation_changed .getValue () .equals (translation))
         this ._translation_changed = translation;
   },
});

Object .defineProperties (PlaneSensor,
{
   ... Core_X3DNode .getStaticProperties ("PlaneSensor", "PointingDeviceSensor", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",         new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "axisRotation",        new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoOffset",          new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "offset",              new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "minPosition",         new x_ite_Fields .SFVec2f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "maxPosition",         new x_ite_Fields .SFVec2f (-1, -1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "trackPoint_changed",  new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "translation_changed", new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isOver",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",            new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const PlaneSensor_default_ = PlaneSensor;
;

/* harmony default export */ const PointingDeviceSensor_PlaneSensor = (Namespace/* default */.A .add ("PlaneSensor", PlaneSensor_default_));
;// ./src/standard/Math/Geometry/Sphere3.js


function Sphere3 (radius = 1, center = Numbers_Vector3 .ZERO)
{
   this .center = new Numbers_Vector3 ();

   this .set (radius, center);
}

Object .assign (Sphere3 .prototype,
{
   copy ()
   {
      const copy = Object .create (Sphere3 .prototype);

      copy .radius = this .radius;
      copy .center = this .center .copy ();

      return copy;
   },
   assign (sphere)
   {
      this .radius = sphere .radius;
      this .center .assign (sphere .center);

      return this;
   },
   equals (sphere)
   {
      return this .radius === sphere .radius && this .center .equals (sphere .center);
   },
   set (radius = 1, center = Numbers_Vector3 .ZERO)
   {
      this .radius = radius;
      this .center .assign (center);

      return this;
   },
   intersectsLine (line, enterPoint, exitPoint)
   {
      // https://github.com/Alexpux/Coin3D/blob/master/src/base/SbSphere.cpp

      const
         { point, direction } = line,
         { center, radius }   = this;

      const
         b = 2 * (point .dot (direction) - center .dot (direction)),
         c = (point .x * point .x + point .y * point .y + point .z * point .z) +
             (center .x * center .x + center .y * center .y + center .z * center .z) -
             2 * point .dot (center) - radius * radius;

      const core = b * b - 4 * c;

      if (core >= 0)
      {
         let
            t1 = (-b + Math .sqrt (core)) / 2,
            t2 = (-b - Math .sqrt (core)) / 2;

         if (t1 > t2)
         {
            const tmp = t1;
            t1 = t2;
            t2 = tmp;
         }

         enterPoint .assign (direction) .multiply (t1) .add (point);
         exitPoint  .assign (direction) .multiply (t2) .add (point);

         return true;
      }
      else
      {
         return false;
      }
   },
   // intersectsTriangle: (() =>
   // {
   //    const
   //       AB = new Vector3 (),
   //       AC = new Vector3 (),
   //       BC = new Vector3 (),
   //       CA = new Vector3 (),
   //       Q1 = new Vector3 (),
   //       Q2 = new Vector3 (),
   //       Q3 = new Vector3 ();

   //    return function (A, B, C)
   //    {
   //       const
   //          P = this .center,
   //          r = this .radius;

   //       A .subtract (P);
   //       B .subtract (P);
   //       C .subtract (P);

   //       // Testing if sphere lies outside the triangle plane.

   //       AB .assign (B) .subtract (A);
   //       AC .assign (C) .subtract (A);

   //       const
   //          rr   = r * r,
   //          V    = AB .cross (AC),
   //          d    = A .dot (V),
   //          e    = V .dot (V),
   //          sep1 = d * d > rr * e;

   //       if (sep1)
   //          return false;

   //       // Testing if sphere lies outside a triangle vertex.
   //       const
   //          aa   = A .dot (A),
   //          ab   = A .dot (B),
   //          ac   = A .dot (C),
   //          bb   = B .dot (B),
   //          bc   = B .dot (C),
   //          cc   = C .dot (C),
   //          sep2 = (aa > rr) && (ab > aa) && (ac > aa),
   //          sep3 = (bb > rr) && (ab > bb) && (bc > bb),
   //          sep4 = (cc > rr) && (ac > cc) && (bc > cc);

   //       if (sep2 || sep3 || sep4)
   //          return false;

   //       // Testing if sphere lies outside a triangle edge.

   //       AB .assign (B) .subtract (A);
   //       BC .assign (C) .subtract (B);
   //       CA .assign (A) .subtract (C);

   //       const
   //          d1   = ab - aa,
   //          d2   = bc - bb,
   //          d3   = ac - cc,
   //          e1   = AB .dot (AB),
   //          e2   = BC .dot (BC),
   //          e3   = CA .dot (CA);

   //       Q1 .assign (A) .multiply (e1) .subtract (AB .multiply (d1));
   //       Q2 .assign (B) .multiply (e2) .subtract (BC .multiply (d2));
   //       Q3 .assign (C) .multiply (e3) .subtract (CA .multiply (d3));

   //       const
   //          QC   = C .multiply (e1) .subtract (Q1),
   //          QA   = A .multiply (e2) .subtract (Q2),
   //          QB   = B .multiply (e3) .subtract (Q3),
   //          sep5 = (Q1 .dot (Q1) > rr * e1 * e1) && (Q1 .dot (QC) > 0),
   //          sep6 = (Q2 .dot (Q2) > rr * e2 * e2) && (Q2 .dot (QA) > 0),
   //          sep7 = (Q3 .dot (Q3) > rr * e3 * e3) && (Q3 .dot (QB) > 0);

   //       if (sep5 || sep6 || sep7)
   //          return false;

   //       return true;
   //    };
   // })(),
   toString ()
   {
      return `${this .radius} ${this .center}`;
   },
});

const Sphere3_default_ = Sphere3;
;

/* harmony default export */ const Geometry_Sphere3 = (Namespace/* default */.A .add ("Sphere3", Sphere3_default_));
;// ./src/x_ite/Components/PointingDeviceSensor/SphereSensor.js













function SphereSensor (executionContext)
{
   PointingDeviceSensor_X3DDragSensorNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SphereSensor);
}

Object .assign (Object .setPrototypeOf (SphereSensor .prototype, PointingDeviceSensor_X3DDragSensorNode .prototype),
{
   initialize ()
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .initialize .call (this);

      this .modelViewMatrix    = new Numbers_Matrix4 ();
      this .invModelViewMatrix = new Numbers_Matrix4 ();

      this .sphere      = null;
      this .zPlane      = null;
      this .behind      = false;
      this .fromVector  = new Numbers_Vector3 ();
      this .startPoint  = new Numbers_Vector3 ();
      this .startOffset = new Numbers_Rotation4 ();
   },
   getTrackPoint (hitRay, trackPoint, behind)
   {
      const exit = new Numbers_Vector3 ();

      if (this .sphere .intersectsLine (hitRay, trackPoint, exit))
      {
         if ((hitRay .point .distance (exit) < hitRay .point .distance (trackPoint)) - behind)
            trackPoint .assign (exit);

         return true;
      }

      return false;
   },
   set_active__ (active, hit, modelViewMatrix, projectionMatrix, viewport)
   {
      PointingDeviceSensor_X3DDragSensorNode .prototype .set_active__ .call (this, active, hit, modelViewMatrix, projectionMatrix, viewport);

      if (this ._isActive .getValue ())
      {
         this .modelViewMatrix    .assign (modelViewMatrix);
         this .invModelViewMatrix .assign (modelViewMatrix) .inverse ();

         const
            hitPoint = this .invModelViewMatrix .multVecMatrix (hit .point .copy ()),
            center   = new Numbers_Vector3 ();

         this .zPlane = new Geometry_Plane3 (center, this .invModelViewMatrix .multDirMatrix (new Numbers_Vector3 (0, 0, 1)) .normalize ()); // Screen aligned Z-plane
         this .sphere = new Geometry_Sphere3 (hitPoint .norm (), center);
         this .behind = this .zPlane .getDistanceToPoint (hitPoint) < 0;

         this .fromVector  .assign (hitPoint);
         this .startPoint  .assign (hitPoint);
         this .startOffset .assign (this ._offset .getValue ());

         this ._trackPoint_changed = hitPoint;
         this ._rotation_changed   = this ._offset .getValue ();
      }
      else
      {
         if (this ._autoOffset .getValue ())
            this ._offset = this ._rotation_changed;
      }
   },
   set_motion__ (hit)
   {
      const
         hitRay     = hit .ray .copy () .multLineMatrix (this .invModelViewMatrix),
         trackPoint = new Numbers_Vector3 ();

      if (this .getTrackPoint (hitRay, trackPoint, this .behind))
      {
         const zAxis = this .invModelViewMatrix .multDirMatrix (new Numbers_Vector3 (0, 0, 1)) .normalize (); // Camera direction

         this .zPlane = new Geometry_Plane3 (trackPoint, zAxis); // Screen aligned Z-plane
      }
      else
      {
         // Find trackPoint on the plane with sphere

         const tangentPoint = new Numbers_Vector3 ();

         this .zPlane .intersectsLine (hitRay, tangentPoint);

         hitRay .set (tangentPoint, this .sphere .center .copy () .subtract (tangentPoint) .normalize ());

         //console .log (hitRay .toString ());

         this .getTrackPoint (hitRay, trackPoint, false);

         // Find trackPoint behind sphere

         const
            triNormal     = Geometry_Triangle3 .normal (this .sphere .center, trackPoint, this .startPoint, new Numbers_Vector3 ()),
            dirFromCenter = trackPoint .copy () .subtract (this .sphere .center) .normalize (),
            normal        = triNormal .copy () .cross (dirFromCenter) .normalize ();

         const point1 = trackPoint .copy () .subtract (normal .multiply (tangentPoint .copy () .subtract (trackPoint) .norm ()));

         hitRay .set (point1, this .sphere .center .copy () .subtract (point1) .normalize ());

         this .getTrackPoint (hitRay, trackPoint, false);
      }

      this ._trackPoint_changed = trackPoint;

      const
         toVector = trackPoint .copy () .subtract (this .sphere .center),
         rotation = Numbers_Rotation4 .fromVectors (this .fromVector, toVector);

      if (this .behind)
         rotation .inverse ();

      this ._rotation_changed = this .startOffset .copy () .multRight (rotation);
   },
});

Object .defineProperties (SphereSensor,
{
   ... Core_X3DNode .getStaticProperties ("SphereSensor", "PointingDeviceSensor", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",        new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",            new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoOffset",         new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "offset",             new x_ite_Fields .SFRotation (0, 1, 0, 0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "trackPoint_changed", new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "rotation_changed",   new x_ite_Fields .SFRotation ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isOver",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",           new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const SphereSensor_default_ = SphereSensor;
;

/* harmony default export */ const PointingDeviceSensor_SphereSensor = (Namespace/* default */.A .add ("SphereSensor", SphereSensor_default_));
;// ./src/x_ite/Components/PointingDeviceSensorComponent.js








const PointingDeviceSensorComponent_default_ = {
   name: "PointingDeviceSensor",
   concreteNodes:
   [
      PointingDeviceSensor_CylinderSensor,
      PointingDeviceSensor_PlaneSensor,
      PointingDeviceSensor_SphereSensor,
      PointingDeviceSensor_TouchSensor,
   ],
   abstractNodes:
   [
      PointingDeviceSensor_X3DDragSensorNode,
      PointingDeviceSensor_X3DPointingDeviceSensorNode,
      PointingDeviceSensor_X3DTouchSensorNode,
   ],
};
;

/* harmony default export */ const PointingDeviceSensorComponent = (Namespace/* default */.A .add ("PointingDeviceSensorComponent", PointingDeviceSensorComponent_default_));
;// ./src/x_ite/Components/Rendering/ClipPlane.js










const ClipPlanes = Utility_ObjectCache (ClipPlaneContainer);

function ClipPlaneContainer ()
{
   this .plane     = new Geometry_Plane3 ();
   this .planeView = new Geometry_Plane3 ();
}

Object .assign (ClipPlaneContainer .prototype,
{
   isClipPlane: true,
   set (clipPlane, modelViewMatrix)
   {
      const
         plane      = this .plane,
         localPlane = clipPlane .plane;

      plane .normal .assign (localPlane);
      plane .distanceFromOrigin = -localPlane .w;

      plane .multRight (modelViewMatrix);
   },
   setShaderUniforms (gl, shaderObject, renderObject)
   {
      const view = renderObject ?.getView ();

      const plane = view
         ? this .planeView .assign (this .plane) .multRight (view .matrix)
         : this .plane;

      gl .uniform4f (shaderObject .x3d_ClipPlane [shaderObject .numClipPlanes ++], ... plane .normal, plane .distanceFromOrigin);
   },
   dispose ()
   {
      ClipPlanes .push (this);
   },
});

function ClipPlane (executionContext)
{
   Core_X3DChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ClipPlane);

   // Private properties

   this .enabled = false;
   this .plane   = new Numbers_Vector4 ();
}

Object .assign (Object .setPrototypeOf (ClipPlane .prototype, Core_X3DChildNode .prototype),
{
   getClipPlaneKey ()
   {
      return 0;
   },
   initialize ()
   {
      Core_X3DChildNode .prototype .initialize .call (this);

      this ._enabled .addInterest ("set_enabled__", this);
      this ._plane   .addInterest ("set_enabled__", this);

      this .set_enabled__ ();
   },
   set_enabled__ ()
   {
      this .plane .assign (this ._plane .getValue ());

      this .enabled = this ._enabled .getValue () && ! this .plane .equals (Numbers_Vector4 .ZERO);
   },
   push (renderObject)
   {
      if (this .enabled)
      {
         const clipPlaneContainer = ClipPlanes .pop ();

         clipPlaneContainer .set (this, renderObject .getModelViewMatrix () .get ());

         renderObject .getLocalObjects ()     .push (clipPlaneContainer);
         renderObject .getLocalObjectsKeys () .push (this .getClipPlaneKey ());
      }
   },
   pop (renderObject)
   {
      if (!this .enabled)
         return;

      this .getBrowser () .getLocalObjects () .add (renderObject .getLocalObjects () .pop ());
      renderObject .getLocalObjectsKeys () .pop ();
   },
});

Object .defineProperties (ClipPlane,
{
   ... Core_X3DNode .getStaticProperties ("ClipPlane", "Rendering", 5, "children", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "plane",    new x_ite_Fields .SFVec4f (0, 1, 0, 0)),
      ]),
      enumerable: true,
   },
});

const ClipPlane_default_ = ClipPlane;
;

/* harmony default export */ const Rendering_ClipPlane = (Namespace/* default */.A .add ("ClipPlane", ClipPlane_default_));
;// ./src/x_ite/Components/Rendering/CoordinateDouble.js







function CoordinateDouble (executionContext)
{
   Rendering_X3DCoordinateNode .call (this, executionContext);

   this .addType (Base_X3DConstants .CoordinateDouble);
}

Object .setPrototypeOf (CoordinateDouble .prototype, Rendering_X3DCoordinateNode .prototype);

Object .defineProperties (CoordinateDouble,
{
   ... Core_X3DNode .getStaticProperties ("CoordinateDouble", "Rendering", 1, "coord", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "point",    new x_ite_Fields .MFVec3d ()),
      ]),
      enumerable: true,
   },
});

const CoordinateDouble_default_ = CoordinateDouble;
;

/* harmony default export */ const Rendering_CoordinateDouble = (Namespace/* default */.A .add ("CoordinateDouble", CoordinateDouble_default_));
;// ./src/x_ite/Components/Rendering/IndexedTriangleFanSet.js







function IndexedTriangleFanSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .IndexedTriangleFanSet);

   // Private properties

   this .triangleIndex = [ ];
}

Object .assign (Object .setPrototypeOf (IndexedTriangleFanSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._set_index .addFieldInterest (this ._index);
      this ._index     .addInterest ("set_index__", this);

      this .set_index__ ();
   },
   set_index__ ()
   {
      // Build coordIndex

      const
         index         = this ._index,
         triangleIndex = this .triangleIndex,
         length        = index .length;

      triangleIndex .length = 0;

      for (let i = 0; i < length; ++ i)
      {
         const first = index [i];

         if (first < 0)
            continue;

         if (++ i < length)
         {
            let second = index [i];

            if (second < 0)
               continue;

            for (++ i; i < length; ++ i)
            {
               const third = index [i];

               if (third < 0)
                  break;

               triangleIndex .push (first, second, third);

               second = third;
            }
         }
      }
   },
   getPolygonIndex (index)
   {
      return this .triangleIndex [index];
   },
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this .triangleIndex .length, 3);
   },
   build ()
   {
      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, this .triangleIndex .length, 3, this .triangleIndex .length);
   },
});

Object .defineProperties (IndexedTriangleFanSet,
{
   ... Core_X3DNode .getStaticProperties ("IndexedTriangleFanSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_index",       new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "index",           new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const IndexedTriangleFanSet_default_ = IndexedTriangleFanSet;
;

/* harmony default export */ const Rendering_IndexedTriangleFanSet = (Namespace/* default */.A .add ("IndexedTriangleFanSet", IndexedTriangleFanSet_default_));
;// ./src/x_ite/Components/Rendering/IndexedTriangleSet.js







function IndexedTriangleSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .IndexedTriangleSet);
}

Object .assign (Object .setPrototypeOf (IndexedTriangleSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   getPolygonIndex (i)
   {
      return this ._index [i];
   },
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this ._index .length, 3);
   },
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._set_index .addFieldInterest (this ._index);
   },
   build ()
   {
      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, this ._index .length, 3, this ._index .length);
   },
});

Object .defineProperties (IndexedTriangleSet,
{
   ... Core_X3DNode .getStaticProperties ("IndexedTriangleSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_index",       new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "index",           new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const IndexedTriangleSet_default_ = IndexedTriangleSet;
;

/* harmony default export */ const Rendering_IndexedTriangleSet = (Namespace/* default */.A .add ("IndexedTriangleSet", IndexedTriangleSet_default_));
;// ./src/x_ite/Components/Rendering/IndexedTriangleStripSet.js







function IndexedTriangleStripSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .IndexedTriangleStripSet);

   // Private properties

   this .triangleIndex = [ ];
}

Object .assign (Object .setPrototypeOf (IndexedTriangleStripSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._set_index .addFieldInterest (this ._index);
      this ._index     .addInterest ("set_index__", this);

      this .set_index__ ();
   },
   set_index__ ()
   {
      // Build coordIndex

      const
         index         = this ._index,
         triangleIndex = this .triangleIndex,
         length        = index .length;

      triangleIndex .length = 0;

      // Build coordIndex

      for (let i = 0; i < length; ++ i)
      {
         let first = index [i];

         if (first < 0)
            continue;

         if (++ i < length)
         {
            let second = index [i];

            if (second < 0)
               continue;

            ++ i;

            for (let face = 0; i < length; ++ i, ++ face)
            {
               const third = index [i];

               if (third < 0)
                  break;

               triangleIndex .push (first, second, third);

               if (face & 1)
                  second = third;

               else
                  first = third;
            }
         }
      }
   },
   getPolygonIndex (index)
   {
      return this .triangleIndex [index];
   },
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this .triangleIndex .length, 3);
   },
   build ()
   {
      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, this .triangleIndex .length, 3, this .triangleIndex .length);
   },
});

Object .defineProperties (IndexedTriangleStripSet,
{
   ... Core_X3DNode .getStaticProperties ("IndexedTriangleStripSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "set_index",       new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "index",           new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const IndexedTriangleStripSet_default_ = IndexedTriangleStripSet;
;

/* harmony default export */ const Rendering_IndexedTriangleStripSet = (Namespace/* default */.A .add ("IndexedTriangleStripSet", IndexedTriangleStripSet_default_));
;// ./src/x_ite/Components/Rendering/TriangleFanSet.js







function TriangleFanSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TriangleFanSet);

   // Private properties

   this .triangleIndex = [ ];
}

Object .assign (Object .setPrototypeOf (TriangleFanSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._fanCount .addInterest ("set_fanCount__", this);

      this .set_fanCount__ ();
   },
   set_fanCount__ ()
   {
      // Build coordIndex

      const
         fanCount      = this ._fanCount,
         triangleIndex = this .triangleIndex;

      triangleIndex .length = 0;

      let index = 0;

      for (const vertexCount of fanCount)
      {
         const count = vertexCount - 1

         for (let i = 1; i < count; ++ i)
         {
            triangleIndex .push (index, index + i, index + i + 1);
         }

         index += vertexCount;
      }
   },
   getPolygonIndex (index)
   {
      return this .triangleIndex [index];
   },
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this .triangleIndex .length, 3);
   },
   build ()
   {
      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, this .triangleIndex .length, 3, this .triangleIndex .length);
   },
});

Object .defineProperties (TriangleFanSet,
{
   ... Core_X3DNode .getStaticProperties ("TriangleFanSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fanCount",        new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const TriangleFanSet_default_ = TriangleFanSet;
;

/* harmony default export */ const Rendering_TriangleFanSet = (Namespace/* default */.A .add ("TriangleFanSet", TriangleFanSet_default_));
;// ./src/x_ite/Components/Rendering/TriangleStripSet.js







function TriangleStripSet (executionContext)
{
   Rendering_X3DComposedGeometryNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TriangleStripSet);

   // Private properties

   this .triangleIndex = [ ];
}

Object .assign (Object .setPrototypeOf (TriangleStripSet .prototype, Rendering_X3DComposedGeometryNode .prototype),
{
   initialize ()
   {
      Rendering_X3DComposedGeometryNode .prototype .initialize .call (this);

      this ._stripCount .addInterest ("set_stripCount__", this);

      this .set_stripCount__ ();
   },
   set_stripCount__ ()
   {
      // Build coordIndex

      const
         stripCount    = this ._stripCount,
         triangleIndex = this .triangleIndex;

      triangleIndex .length = 0;

      let index = 0;

      for (const vertexCount of stripCount)
      {
         const count = vertexCount - 2;

         for (let i = 0; i < count; ++ i)
         {
            const odd = i & 1;

            triangleIndex .push (index + (odd ? i + 1 : i),
                                 index + (odd ? i : i + 1),
                                 index + (i + 2));
         }

         index += vertexCount;
      }
   },
   getPolygonIndex (index)
   {
      return this .triangleIndex [index];
   },
   getVerticesPerPolygon ()
   {
      return 3;
   },
   getNumVertices ()
   {
      return this .checkVertexCount (this .triangleIndex .length, 3);
   },
   build ()
   {
      Rendering_X3DComposedGeometryNode .prototype .build .call (this, 3, this .triangleIndex .length, 3, this .triangleIndex .length);
   },
});

Object .defineProperties (TriangleStripSet,
{
   ... Core_X3DNode .getStaticProperties ("TriangleStripSet", "Rendering", 3, "geometry", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "solid",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "ccw",             new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "colorPerVertex",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "normalPerVertex", new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "stripCount",      new x_ite_Fields .MFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "attrib",          new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "fogCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "color",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "texCoord",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "tangent",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "normal",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coord",           new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const TriangleStripSet_default_ = TriangleStripSet;
;

/* harmony default export */ const Rendering_TriangleStripSet = (Namespace/* default */.A .add ("TriangleStripSet", TriangleStripSet_default_));
;// ./src/x_ite/Components/RenderingComponent.js
























const RenderingComponent_default_ = {
   name: "Rendering",
   concreteNodes:
   [
      Rendering_ClipPlane,
      Rendering_Color,
      Rendering_ColorRGBA,
      Rendering_Coordinate,
      Rendering_CoordinateDouble,
      Rendering_IndexedLineSet,
      Rendering_IndexedTriangleFanSet,
      Rendering_IndexedTriangleSet,
      Rendering_IndexedTriangleStripSet,
      Rendering_LineSet,
      Rendering_Normal,
      Rendering_PointSet,
      Rendering_Tangent,
      Rendering_TriangleFanSet,
      Rendering_TriangleSet,
      Rendering_TriangleStripSet,
   ],
   abstractNodes:
   [
      Rendering_X3DColorNode,
      Rendering_X3DComposedGeometryNode,
      Rendering_X3DCoordinateNode,
      Rendering_X3DGeometricPropertyNode,
      Rendering_X3DGeometryNode,
      Rendering_X3DNormalNode,
      Rendering_X3DTangentNode,
   ],
};
;

/* harmony default export */ const RenderingComponent = (Namespace/* default */.A .add ("RenderingComponent", RenderingComponent_default_));
;// ./src/x_ite/Components/Shaders/X3DVertexAttributeNode.js





function X3DVertexAttributeNode (executionContext)
{
   Rendering_X3DGeometricPropertyNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DVertexAttributeNode);

   this .addChildObjects (Base_X3DConstants .outputOnly, "attribute_changed", new x_ite_Fields .SFTime ());
}

Object .assign (Object .setPrototypeOf (X3DVertexAttributeNode .prototype, Rendering_X3DGeometricPropertyNode .prototype),
{
   initialize ()
   {
      Rendering_X3DGeometricPropertyNode .prototype .initialize .call (this);

      this ._name .addInterest ("set_attribute__", this);
   },
   set_attribute__ ()
   {
      this ._attribute_changed = this .getBrowser () .getCurrentTime ();
   },
});

Object .defineProperties (X3DVertexAttributeNode, Core_X3DNode .getStaticProperties ("X3DVertexAttributeNode", "Shaders", 1));

const X3DVertexAttributeNode_default_ = X3DVertexAttributeNode;
;

/* harmony default export */ const Shaders_X3DVertexAttributeNode = (Namespace/* default */.A .add ("X3DVertexAttributeNode", X3DVertexAttributeNode_default_));
;// ./src/x_ite/Components/Shaders/FloatVertexAttribute.js








function FloatVertexAttribute (executionContext)
{
   Shaders_X3DVertexAttributeNode .call (this, executionContext);

   this .addType (Base_X3DConstants .FloatVertexAttribute);
}

Object .assign (Object .setPrototypeOf (FloatVertexAttribute .prototype, Shaders_X3DVertexAttributeNode .prototype),
{
   initialize ()
   {
      Shaders_X3DVertexAttributeNode .prototype .initialize .call (this);

      this ._numComponents .addInterest ("set_numComponents__", this);
      this ._numComponents .addInterest ("set_attribute__",     this);
      this ._value         .addInterest ("set_value__",         this);

      this .set_numComponents__ ();
      this .set_value__ ();
   },
   set_numComponents__ ()
   {
      this .numComponents = Math_Algorithm .clamp (this ._numComponents .getValue (), 1, 4);
   },
   set_value__ ()
   {
      this .value  = this ._value .getValue ();
      this .length = this ._value .length;
   },
   addValue (index, array)
   {
      const value = this .value;

      let
         first = index * this .numComponents,
         last  = first + this .numComponents;

      if (last <= this .length)
      {
         for (; first < last; ++ first)
            array .push (value [first]);
      }
      else if (this .numComponents <= this .length)
      {
         last  = value .length;
         first = last - this .numComponents;

         for (; first < last; ++ first)
            array .push (value [first]);
      }
      else
      {
         for (; first < last; ++ first)
            array .push (0);
      }
   },
   enable (gl, shaderNode, buffer)
   {
      shaderNode .enableFloatAttrib (gl, this ._name .getValue (), buffer, this .numComponents, 0, 0);
   },
});

Object .defineProperties (FloatVertexAttribute,
{
   ... Core_X3DNode .getStaticProperties ("FloatVertexAttribute", "Shaders", 1, "attrib", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "name",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "numComponents", new x_ite_Fields .SFInt32 (4)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "value",         new x_ite_Fields .MFFloat ()),
      ]),
      enumerable: true,
   },
});

const FloatVertexAttribute_default_ = FloatVertexAttribute;
;

/* harmony default export */ const Shaders_FloatVertexAttribute = (Namespace/* default */.A .add ("FloatVertexAttribute", FloatVertexAttribute_default_));
;// ./src/x_ite/Components/Shaders/Matrix3VertexAttribute.js








function Matrix3VertexAttribute (executionContext)
{
   Shaders_X3DVertexAttributeNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Matrix3VertexAttribute);
}

Object .assign (Object .setPrototypeOf (Matrix3VertexAttribute .prototype, Shaders_X3DVertexAttributeNode .prototype),
{
   initialize ()
   {
      Shaders_X3DVertexAttributeNode .prototype .initialize .call (this);

      this ._value .addInterest ("set_value__", this);

      this .set_value__ ();
   },
   set_value__ ()
   {
      this .value  = this ._value .getValue ();
      this .length = this ._value .length;
   },
   addValue (index, array)
   {
      if (index < this .length)
      {
         const value = this .value;

         for (let i = index * 9, l = i + 9; i < l; ++ i)
            array .push (value [i]);
      }
      else if (this .length)
      {
         const value = this .value;

         index = this .length - 1;

         for (let i = index * 9, l = i + 9; i < l; ++ i)
            array .push (value [i]);
      }
      else
      {
         const value = Numbers_Matrix3 .IDENTITY;

         for (let i = 0; i < 9; ++ i)
            array .push (value [i]);
      }
   },
   enable (gl, shaderNode, buffer)
   {
      shaderNode .enableMatrix3Attrib (gl, this ._name .getValue (), buffer, 0, 0);
   },
});

Object .defineProperties (Matrix3VertexAttribute,
{
   ... Core_X3DNode .getStaticProperties ("Matrix3VertexAttribute", "Shaders", 1, "attrib", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "name",     new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "value",    new x_ite_Fields .MFMatrix3f ()),
      ]),
      enumerable: true,
   },
});

const Matrix3VertexAttribute_default_ = Matrix3VertexAttribute;
;

/* harmony default export */ const Shaders_Matrix3VertexAttribute = (Namespace/* default */.A .add ("Matrix3VertexAttribute", Matrix3VertexAttribute_default_));
;// ./src/x_ite/Components/Shaders/Matrix4VertexAttribute.js







function Matrix4VertexAttribute (executionContext)
{
   Shaders_X3DVertexAttributeNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Matrix4VertexAttribute);
}

Object .assign (Object .setPrototypeOf (Matrix4VertexAttribute .prototype, Shaders_X3DVertexAttributeNode .prototype),
{
   initialize ()
   {
      Shaders_X3DVertexAttributeNode .prototype .initialize .call (this);

      this ._value .addInterest ("set_value__", this);

      this .set_value__ ();
   },
   set_value__ ()
   {
      this .value  = this ._value .getValue ();
      this .length = this ._value .length;
   },
   addValue (index, array)
   {
      if (index < this .length)
      {
         const value = this .value;

         for (let i = index * 16, l = i + 16; i < l; ++ i)
            array .push (value [i]);
      }
      else if (this .length)
      {
         const value = this .value;

         index = this .length - 1;

         for (let i = index * 16, l = i + 16; i < l; ++ i)
            array .push (value [i]);
      }
      else
      {
         const value = Matrix4 .IDENTITY;

         for (let i = 0; i < 16; ++ i)
            array .push (value [i]);
      }
   },
   enable (gl, shaderNode, buffer)
   {
      shaderNode .enableMatrix4Attrib (gl, this ._name .getValue (), buffer, 0, 0);
   },
});

Object .defineProperties (Matrix4VertexAttribute,
{
   ... Core_X3DNode .getStaticProperties ("Matrix4VertexAttribute", "Shaders", 1, "attrib", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "name",     new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "value",    new x_ite_Fields .MFMatrix4f ()),
      ]),
      enumerable: true,
   },
});

const Matrix4VertexAttribute_default_ = Matrix4VertexAttribute;
;

/* harmony default export */ const Shaders_Matrix4VertexAttribute = (Namespace/* default */.A .add ("Matrix4VertexAttribute", Matrix4VertexAttribute_default_));
;// ./src/x_ite/Components/Shaders/PackagedShader.js









/**
 * THIS NODE IS NOT SUPPORTED.
 */

function PackagedShader (executionContext)
{
   Shaders_X3DShaderNode               .call (this, executionContext);
   Networking_X3DUrlObject                .call (this, executionContext);
   Shaders_X3DProgrammableShaderObject .call (this, executionContext);

   this .addType (Base_X3DConstants .PackagedShader);
}

Object .assign (Object .setPrototypeOf (PackagedShader .prototype, Shaders_X3DShaderNode .prototype),
   Networking_X3DUrlObject .prototype,
   Shaders_X3DProgrammableShaderObject .prototype,
{
   getSourceText ()
   {
      return this ._url;
   },
   requestImmediateLoad (cache = true)
   { },
   initialize ()
   {
      Shaders_X3DShaderNode               .prototype .initialize .call (this);
      Networking_X3DUrlObject                .prototype .initialize .call (this);
      Shaders_X3DProgrammableShaderObject .prototype .initialize .call (this);
   },
   dispose ()
   {
      Shaders_X3DProgrammableShaderObject .prototype .dispose .call (this);
      Networking_X3DUrlObject                .prototype .dispose .call (this);
      Shaders_X3DShaderNode               .prototype .dispose .call (this);
   },
});

Object .defineProperties (PackagedShader,
{
   ... Core_X3DNode .getStaticProperties ("PackagedShader", "Shaders", 1, "shaders", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "activate",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isSelected",           new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isValid",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "language",             new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
      ]),
      enumerable: true,
   },
});

const PackagedShader_default_ = PackagedShader;
;

/* harmony default export */ const Shaders_PackagedShader = (Namespace/* default */.A .add ("PackagedShader", PackagedShader_default_));
;// ./src/x_ite/Components/Shaders/ProgramShader.js







/**
 * THIS NODE IS NOT SUPPORTED.
 */

function ProgramShader (executionContext)
{
   Shaders_X3DShaderNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ProgramShader);
}

Object .setPrototypeOf (ProgramShader .prototype, Shaders_X3DShaderNode .prototype);

Object .defineProperties (ProgramShader,
{
   ... Core_X3DNode .getStaticProperties ("ProgramShader", "Shaders", 1, "shaders", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOnly,      "activate",   new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isSelected", new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isValid",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "language",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "programs",   new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ProgramShader_default_ = ProgramShader;
;

/* harmony default export */ const Shaders_ProgramShader = (Namespace/* default */.A .add ("ProgramShader", ProgramShader_default_));
;// ./src/x_ite/Components/Shaders/ShaderProgram.js








/**
 * THIS NODE IS NOT SUPPORTED.
 */

function ShaderProgram (executionContext)
{
   Core_X3DNode                     .call (this, executionContext);
   Networking_X3DUrlObject                .call (this, executionContext);
   Shaders_X3DProgrammableShaderObject .call (this, executionContext);

   this .addType (Base_X3DConstants .ShaderProgram);
}

Object .assign (Object .setPrototypeOf (ShaderProgram .prototype, Core_X3DNode .prototype),
   Networking_X3DUrlObject .prototype,
   Shaders_X3DProgrammableShaderObject .prototype,
{
   getSourceText ()
   {
      return this ._url;
   },
   requestImmediateLoad (cache = true)
   { },
   initialize ()
   {
      Core_X3DNode                     .prototype .initialize .call (this);
      Networking_X3DUrlObject                .prototype .initialize .call (this);
      Shaders_X3DProgrammableShaderObject .prototype .initialize .call (this);
   },
   dispose ()
   {
      Shaders_X3DProgrammableShaderObject .prototype .dispose .call (this);
      Networking_X3DUrlObject                .prototype .dispose .call (this);
      Core_X3DNode                     .prototype .dispose .call (this);
   },
});

Object .defineProperties (ShaderProgram,
{
   ... Core_X3DNode .getStaticProperties ("ShaderProgram", "Shaders", 1, "programs", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "type",                 new x_ite_Fields .SFString ("VERTEX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
      ]),
      enumerable: true,
   },
});

const ShaderProgram_default_ = ShaderProgram;
;

/* harmony default export */ const Shaders_ShaderProgram = (Namespace/* default */.A .add ("ShaderProgram", ShaderProgram_default_));
;// ./src/x_ite/Components/ShadersComponent.js












const ShadersComponent_default_ = {
   name: "Shaders",
   concreteNodes:
   [
      Shaders_ComposedShader,
      Shaders_FloatVertexAttribute,
      Shaders_Matrix3VertexAttribute,
      Shaders_Matrix4VertexAttribute,
      Shaders_PackagedShader,
      Shaders_ProgramShader,
      Shaders_ShaderPart,
      Shaders_ShaderProgram,
   ],
   abstractNodes:
   [
      Shaders_X3DProgrammableShaderObject,
      Shaders_X3DShaderNode,
      Shaders_X3DVertexAttributeNode,
   ],
};
;

/* harmony default export */ const ShadersComponent = (Namespace/* default */.A .add ("ShadersComponent", ShadersComponent_default_));
;// ./src/x_ite/Components/Shape/AcousticProperties.js







function AcousticProperties (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .AcousticProperties);
}

Object .setPrototypeOf (AcousticProperties .prototype, Shape_X3DAppearanceChildNode .prototype);

Object .defineProperties (AcousticProperties,
{
   ... Core_X3DNode .getStaticProperties ("AcousticProperties", "Shape", 5, "acousticProperties", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diffuse",     new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "specular",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "refraction",  new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "absorption",  new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const AcousticProperties_default_ = AcousticProperties;
;

/* harmony default export */ const Shape_AcousticProperties = (Namespace/* default */.A .add ("AcousticProperties", AcousticProperties_default_));
;// ./src/x_ite/Components/Shape/FillProperties.js







function FillProperties (executionContext)
{
   Shape_X3DAppearanceChildNode .call (this, executionContext);

   this .addType (Base_X3DConstants .FillProperties);

   this .addChildObjects (Base_X3DConstants .outputOnly, "transparent", new x_ite_Fields .SFBool ());

   // Private properties

   this .hatchColor = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (FillProperties .prototype, Shape_X3DAppearanceChildNode .prototype),
{
   initialize ()
   {
      Shape_X3DAppearanceChildNode .prototype .initialize .call (this);

      this ._filled     .addInterest ("set_filled__",     this);
      this ._hatched    .addInterest ("set_hatched__",    this);
      this ._hatchColor .addInterest ("set_hatchColor__", this);
      this ._hatchStyle .addInterest ("set_hatchStyle__", this);

      this .set_filled__ ();
      this .set_hatched__ ();
      this .set_hatchColor__ ();
      this .set_hatchStyle__ ();

      // Preload texture.
      this .getBrowser () .getHatchStyleTexture (this .hatchStyle);
   },
   getStyleKey ()
   {
      return this .hatched ? 2 : 1;
   },
   set_filled__ ()
   {
      this .filled = this ._filled .getValue ();

      this .setTransparent (!this .filled);
   },
   set_hatched__ ()
   {
      this .hatched = this ._hatched .getValue ();
   },
   set_hatchColor__ ()
   {
      this .hatchColor [0] = this ._hatchColor [0];
      this .hatchColor [1] = this ._hatchColor [1];
      this .hatchColor [2] = this ._hatchColor [2];
   },
   set_hatchStyle__ ()
   {
      let hatchStyle = this ._hatchStyle .getValue ();

      if (hatchStyle < 1 || hatchStyle > 19)
         hatchStyle = 1;

      this .hatchStyle = hatchStyle;
   },
   setTransparent (value)
   {
      if (!!value !== this ._transparent .getValue ())
         this ._transparent = value;
   },
   isTransparent ()
   {
      return this ._transparent .getValue ();
   },
   setShaderUniforms (gl, shaderObject)
   {
      const hatched = this .hatched;

      gl .uniform1i (shaderObject .x3d_FillPropertiesFilled,  this .filled);
      gl .uniform1i (shaderObject .x3d_FillPropertiesHatched, hatched);

      if (hatched)
      {
         const
            browser     = this .getBrowser (),
            texture     = browser .getHatchStyleTexture (this .hatchStyle),
            textureUnit = browser .popTextureUnit ();

         gl .uniform3fv (shaderObject .x3d_FillPropertiesHatchColor, this .hatchColor);
         gl .uniform1f  (shaderObject .x3d_FillPropertiesScale,      browser .getRenderingProperty ("ContentScale"));

         gl .activeTexture (gl .TEXTURE0 + textureUnit);
         gl .bindTexture (gl .TEXTURE_2D, texture .getTexture ());
         gl .uniform1i (shaderObject .x3d_FillPropertiesTexture, textureUnit);
      }
   },
});

Object .defineProperties (FillProperties,
{
   ... Core_X3DNode .getStaticProperties ("FillProperties", "Shape", 3, "fillProperties", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "filled",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "hatched",    new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "hatchStyle", new x_ite_Fields .SFInt32 (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "hatchColor", new x_ite_Fields .SFColor (1, 1, 1)),
      ]),
      enumerable: true,
   },
});

const FillProperties_default_ = FillProperties;
;

/* harmony default export */ const Shape_FillProperties = (Namespace/* default */.A .add ("FillProperties", FillProperties_default_));
;// ./src/x_ite/Components/Shape/Material.js










function Material (executionContext)
{
   Shape_X3DOneSidedMaterialNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Material);

   // Legacy

   if (executionContext .getSpecificationVersion () <= 3.3)
      this .getMaterialKey = getMaterialKey;

   // Private properties

   this .diffuseColorArray  = new Float32Array (3);
   this .specularColorArray = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (Material .prototype, Shape_X3DOneSidedMaterialNode .prototype),
{
   initialize ()
   {
      Shape_X3DOneSidedMaterialNode .prototype .initialize .call (this);

      this ._ambientIntensity  .addInterest ("set_ambientIntensity__",  this);
      this ._ambientTexture    .addInterest ("set_ambientTexture__",    this);
      this ._diffuseColor      .addInterest ("set_diffuseColor__",      this);
      this ._diffuseTexture    .addInterest ("set_diffuseTexture__",    this);
      this ._diffuseTexture    .addInterest ("set_transparent__",       this);
      this ._specularColor     .addInterest ("set_specularColor__",     this);
      this ._specularTexture   .addInterest ("set_specularTexture__",   this);
      this ._shininess         .addInterest ("set_shininess__",         this);
      this ._shininessTexture  .addInterest ("set_shininessTexture__",  this);
      this ._occlusionStrength .addInterest ("set_occlusionStrength__", this);
      this ._occlusionTexture  .addInterest ("set_occlusionTexture__",  this);

      this .set_ambientIntensity__ ();
      this .set_ambientTexture__ ();
      this .set_diffuseColor__ ();
      this .set_diffuseTexture__ ();
      this .set_specularColor__ ();
      this .set_specularTexture__ ();
      this .set_shininess__ ();
      this .set_shininessTexture__ ();
      this .set_occlusionStrength__ ();
      this .set_occlusionTexture__ ();
      this .set_transparent__ ();
   },
   getMaterialKey ()
   {
      return 2;
   },
   getTextureIndices: (() =>
   {
      let i = 0;

      const textureIndices = {
         AMBIENT_TEXTURE: i ++,
         DIFFUSE_TEXTURE: i ++,
         SPECULAR_TEXTURE: i ++,
         EMISSIVE_TEXTURE: i ++,
         SHININESS_TEXTURE: i ++,
         OCCLUSION_TEXTURE: i ++,
         NORMAL_TEXTURE: i ++,
      };

      return function ()
      {
         return textureIndices;
      };
   })(),
   getBaseTexture ()
   {
      return this .diffuseTextureNode;
   },
   set_ambientIntensity__ ()
   {
      this .ambientIntensity = Math_Algorithm .clamp (this ._ambientIntensity .getValue (), 0, 1);
   },
   set_ambientTexture__ ()
   {
      const index = this .getTextureIndices () .AMBIENT_TEXTURE;

      this .ambientTextureNode ?._linear .removeInterest ("addTexture", this);

      this .ambientTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._ambientTexture);

      this .ambientTextureNode ?._linear .addInterest ("addTexture", this, index, this .ambientTextureNode);

      this .addTexture (index, this .ambientTextureNode);
   },
   set_diffuseColor__ ()
   {
      this .diffuseColorArray .set (this ._diffuseColor .getValue ());
   },
   set_diffuseTexture__ ()
   {
      const index = this .getTextureIndices () .DIFFUSE_TEXTURE;

      if (this .diffuseTextureNode)
      {
         this .diffuseTextureNode ._transparent .removeInterest ("set_transparent__", this);
         this .diffuseTextureNode ._linear      .removeInterest ("addTexture",        this);
      }

      this .diffuseTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._diffuseTexture);

      if (this .diffuseTextureNode)
      {
         this .diffuseTextureNode ._transparent .addInterest ("set_transparent__", this);
         this .diffuseTextureNode ._linear      .addInterest ("addTexture",        this, index, this .diffuseTextureNode);
      }

      this .addTexture (index, this .diffuseTextureNode);
   },
   set_specularColor__ ()
   {
      this .specularColorArray .set (this ._specularColor .getValue ());
   },
   set_specularTexture__ ()
   {
      const index = this .getTextureIndices () .SPECULAR_TEXTURE;

      this .specularTextureNode ?._linear .removeInterest ("addTexture", this);

      this .specularTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._specularTexture);

      this .specularTextureNode ?._linear .addInterest ("addTexture", this, index, this .specularTextureNode);

      this .addTexture (index, this .specularTextureNode);
   },
   set_shininess__ ()
   {
      this .shininess = Math_Algorithm .clamp (this ._shininess .getValue (), 0, 1);
   },
   set_shininessTexture__ ()
   {
      this .shininessTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._shininessTexture);

      this .addTexture (this .getTextureIndices () .SHININESS_TEXTURE, this .shininessTextureNode);
   },
   set_occlusionStrength__ ()
   {
      this .occlusionStrength = Math_Algorithm .clamp (this ._occlusionStrength .getValue (), 0, 1);
   },
   set_occlusionTexture__ ()
   {
      this .occlusionTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._occlusionTexture);

      this .addTexture (this .getTextureIndices () .OCCLUSION_TEXTURE, this .occlusionTextureNode);
   },
   createShader (key, geometryContext, renderContext)
   {
      const
         browser = this .getBrowser (),
         options = this .getShaderOptions (geometryContext, renderContext);

      let shaderNode;

      if (geometryContext .hasNormals)
      {
         if (+this .getTextureBits ())
         {
            this .ambientTextureNode   ?.getShaderOptions (options, "AMBIENT");
            this .diffuseTextureNode   ?.getShaderOptions (options, "DIFFUSE");
            this .specularTextureNode  ?.getShaderOptions (options, "SPECULAR");
            this .shininessTextureNode ?.getShaderOptions (options, "SHININESS");
            this .occlusionTextureNode ?.getShaderOptions (options, "OCCLUSION");
         }

         switch (this .getMaterialKey ())
         {
            case 1:
            {
               options .push ("X3D_GOURAUD_MATERIAL");

               shaderNode = browser .createShader ({
                  name: "Gouraud",
                  vertexShader: "Default",
                  fragmentShader: "Material",
                  options,
               });

               break;
            }
            case 2:
            {
               options .push ("X3D_PHONG_MATERIAL");

               shaderNode = browser .createShader ({
                  name: "Phong",
                  vertexShader: "Default",
                  fragmentShader: "Material",
                  options,
               });

               break;
            }
         }
      }
      else
      {
         // If the Material node is used together with unlit points and lines, geometry shall be rendered as unlit and only the emissiveColor is used.

         options .push ("X3D_UNLIT_MATERIAL");

         shaderNode = browser .createShader ({
            name: "Unlit",
            vertexShader: "Default",
            fragmentShader: "Unlit",
            options,
         });
      }

      browser .getShaders () .set (key, shaderNode);

      return shaderNode;
   },
   setShaderUniforms (gl, shaderObject, textureTransformMapping, textureCoordinateMapping)
   {
      Shape_X3DOneSidedMaterialNode .prototype .setShaderUniforms .call (this, gl, shaderObject, textureTransformMapping, textureCoordinateMapping);

      gl .uniform1f  (shaderObject .x3d_AmbientIntensity, this .ambientIntensity);
      gl .uniform3fv (shaderObject .x3d_DiffuseColor,     this .diffuseColorArray);
      gl .uniform3fv (shaderObject .x3d_SpecularColor,    this .specularColorArray);
      gl .uniform1f  (shaderObject .x3d_Shininess,        this .shininess);

      if (!+this .getTextureBits ())
         return;

      this .ambientTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_AmbientTexture,
         this ._ambientTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      this .diffuseTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_DiffuseTexture,
         this ._diffuseTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      this .specularTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_SpecularTexture,
         this ._specularTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      this .shininessTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_ShininessTexture,
         this ._shininessTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      if (this .occlusionTextureNode)
      {
         gl .uniform1f (shaderObject .x3d_OcclusionStrength, this .occlusionStrength);

         this .occlusionTextureNode .setNamedShaderUniforms (gl,
            shaderObject .x3d_OcclusionTexture,
            this ._occlusionTextureMapping .getValue (),
            textureTransformMapping,
            textureCoordinateMapping);
      }
   },
});

function getMaterialKey ()
{
   switch (this .getBrowser () .getBrowserOptions () .getShading ())
   {
      default:
         return 1;
      case Core_Shading .PHONG:
         return 2;
   }
}

Object .defineProperties (Material,
{
   ... Core_X3DNode .getStaticProperties ("Material", "Shape", 1, "material", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",                 new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ambientIntensity",         new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ambientTextureMapping",    new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ambientTexture",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diffuseColor",             new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diffuseTextureMapping",    new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diffuseTexture",           new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "specularColor",            new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "specularTextureMapping",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "specularTexture",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveColor",            new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTextureMapping",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTexture",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "shininess",                new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "shininessTextureMapping",  new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "shininessTexture",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionStrength",        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionTextureMapping",  new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionTexture",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalScale",              new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTextureMapping",     new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTexture",            new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency",             new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const Material_default_ = Material;
;

/* harmony default export */ const Shape_Material = (Namespace/* default */.A .add ("Material", Material_default_));
;// ./src/x_ite/Components/Shape/PhysicalMaterial.js









function PhysicalMaterial (executionContext)
{
   Shape_X3DOneSidedMaterialNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PhysicalMaterial);

   // Private properties

   this .baseColorArray = new Float32Array (3);
   this .extensionNodes = [ ];
}

Object .assign (Object .setPrototypeOf (PhysicalMaterial .prototype, Shape_X3DOneSidedMaterialNode .prototype),
{
   initialize ()
   {
      Shape_X3DOneSidedMaterialNode .prototype .initialize .call (this);

      this ._baseColor                .addInterest ("set_baseColor__",                this);
      this ._baseTexture              .addInterest ("set_baseTexture__",              this);
      this ._baseTexture              .addInterest ("set_transparent__",              this);
      this ._metallic                 .addInterest ("set_metallic__",                 this);
      this ._roughness                .addInterest ("set_roughness__",                this);
      this ._metallicRoughnessTexture .addInterest ("set_metallicRoughnessTexture__", this);
      this ._occlusionStrength        .addInterest ("set_occlusionStrength__",        this);
      this ._occlusionTexture         .addInterest ("set_occlusionTexture__",         this);
      this ._extensions               .addInterest ("set_extensions__",               this);

      this .set_baseColor__ ();
      this .set_baseTexture__ ();
      this .set_metallic__ ();
      this .set_roughness__ ();
      this .set_metallicRoughnessTexture__ ();
      this .set_occlusionStrength__ ();
      this .set_occlusionTexture__ ();
      this .set_extensions__ ();
      this .set_transparent__ ();
   },
   isPhysical ()
   {
      return true;
   },
   getMaterialKey ()
   {
      return this .materialKey;
   },
   getTextureIndices: (() =>
   {
      let i = 0;

      const textureIndices = {
         BASE_TEXTURE: i ++,
         EMISSIVE_TEXTURE: i ++,
         METALLIC_ROUGHNESS_TEXTURE: i ++,
         OCCLUSION_TEXTURE: i ++,
         NORMAL_TEXTURE: i ++,
         LENGTH: i,
      };

      return function ()
      {
         return textureIndices;
      };
   })(),
   getBaseTexture ()
   {
      return this .baseTextureNode;
   },
   set_baseColor__ ()
   {
      this .baseColorArray .set (this ._baseColor .getValue ());
   },
   set_baseTexture__ ()
   {
      const index = this .getTextureIndices () .BASE_TEXTURE;

      if (this .baseTextureNode)
      {
         this .baseTextureNode ._transparent .removeInterest ("set_transparent__", this);
         this .baseTextureNode ._linear      .removeInterest ("addTexture",        this);
      }

      this .baseTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._baseTexture);

      if (this .baseTextureNode)
      {
         this .baseTextureNode ._transparent .addInterest ("set_transparent__", this);
         this .baseTextureNode ._linear      .addInterest ("addTexture",        this, index, this .baseTextureNode);
      }

      this .addTexture (index, this .baseTextureNode);
   },
   set_metallic__ ()
   {
      this .metallic = Math_Algorithm .clamp (this ._metallic .getValue (), 0, 1);
   },
   set_roughness__ ()
   {
      this .roughness = Math_Algorithm .clamp (this ._roughness .getValue (), 0, 1);
   },
   set_metallicRoughnessTexture__ ()
   {
      this .metallicRoughnessTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._metallicRoughnessTexture);

      this .addTexture (this .getTextureIndices () .METALLIC_ROUGHNESS_TEXTURE, this .metallicRoughnessTextureNode);
   },
   set_occlusionStrength__ ()
   {
      this .occlusionStrength = Math_Algorithm .clamp (this ._occlusionStrength .getValue (), 0, 1);
   },
   set_occlusionTexture__ ()
   {
      this .occlusionTextureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, this ._occlusionTexture);

      this .addTexture (this .getTextureIndices () .OCCLUSION_TEXTURE, this .occlusionTextureNode);
   },
   set_extensions__ ()
   {
      const extensionNodes = this .extensionNodes;

      for (const extensionNode of extensionNodes)
      {
         extensionNode .removeInterest ("set_extensionsKey__",    this);
         extensionNode .removeInterest ("set_renderedTextures__", this);
      }

      extensionNodes .length = 0;

      for (const node of this ._extensions)
      {
         const extensionNode = Base_X3DCast (Base_X3DConstants .X3DMaterialExtensionNode, node);

         if (extensionNode)
            extensionNodes .push (extensionNode);
      }

      extensionNodes .sort ((a, b) => a .getExtensionKey () - b .getExtensionKey ());

      for (const extensionNode of extensionNodes)
      {
         extensionNode .addInterest ("set_extensionsKey__",    this);
         extensionNode .addInterest ("set_renderedTextures__", this);
      }

      this .setTransmission (extensionNodes .some (extensionNode => extensionNode .getType () .includes (Base_X3DConstants .TransmissionMaterialExtension)));

      this .setVolumeScatter (extensionNodes .some (extensionNode => extensionNode .getType () .includes (Base_X3DConstants .VolumeScatterMaterialExtension)) && extensionNodes .some (extensionNode => extensionNode .getType () .includes (Base_X3DConstants .VolumeMaterialExtension)));

      this .set_extensionsKey__ ();
      this .set_renderedTextures__ ();
   },
   set_extensionsKey__ ()
   {
      // Make sure to use the right base for the extension key depending on the number of extensions!

      const extensionsKey = this .extensionNodes
         .map (extensionNode => `${extensionNode .getExtensionKey () .toString (16)}${extensionNode .getTextureBits () .toString (16)}`)
         .join ("");

      this .materialKey = `[3.${extensionsKey}]`;
   },
   set_renderedTextures__ ()
   {
      this ._renderedTextures = this .getBrowser () .getCurrentTime ();
   },
   getRenderedTextures (renderedTextures)
   {
      Shape_X3DOneSidedMaterialNode .prototype .getRenderedTextures .call (this, renderedTextures);

      for (const extensionNode of this .extensionNodes)
         extensionNode .getRenderedTextures (renderedTextures);
   },
   createShader (key, geometryContext, renderContext)
   {
      const
         browser  = this .getBrowser (),
         options  = this .getShaderOptions (geometryContext, renderContext),
         uniforms = [ ];

      for (const extensionNode of this .extensionNodes)
         extensionNode .getShaderOptions (options);

      options .push ("X3D_PHYSICAL_MATERIAL", "X3D_MATERIAL_METALLIC_ROUGHNESS");

      if (+this .getTextureBits ())
      {
         this .baseTextureNode              ?.getShaderOptions (options, "BASE");
         this .metallicRoughnessTextureNode ?.getShaderOptions (options, "METALLIC_ROUGHNESS");
         this .occlusionTextureNode         ?.getShaderOptions (options, "OCCLUSION");
      }

      for (const extensionNode of this .extensionNodes)
         extensionNode .getShaderUniforms (uniforms);

      const shaderNode = browser .createShader ({
         name: "Physical",
         vertexShader: "Default",
         fragmentShader: "Physical",
         options,
         uniforms,
      });

      browser .getShaders () .set (key, shaderNode);

      shaderNode .volumeScatterPass         = options .includes ("X3D_VOLUME_SCATTER_PASS");
      shaderNode .transmissionBackfacesPass = options .includes ("X3D_TRANSMISSION_BACKFACES_PASS");

      return shaderNode;
   },
   setShaderUniforms (gl, shaderObject, textureTransformMapping, textureCoordinateMapping)
   {
      Shape_X3DOneSidedMaterialNode .prototype .setShaderUniforms .call (this, gl, shaderObject, textureTransformMapping, textureCoordinateMapping);

      for (const extensionNode of this .extensionNodes)
         extensionNode .setShaderUniforms (gl, shaderObject, textureTransformMapping, textureCoordinateMapping);

      gl .uniform3fv (shaderObject .x3d_BaseColor, this .baseColorArray);
      gl .uniform1f  (shaderObject .x3d_Metallic,  this .metallic);
      gl .uniform1f  (shaderObject .x3d_Roughness, this .roughness);

      if (!+this .getTextureBits ())
         return;

      this .baseTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_BaseTexture,
         this ._baseTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      this .metallicRoughnessTextureNode ?.setNamedShaderUniforms (gl,
         shaderObject .x3d_MetallicRoughnessTexture,
         this ._metallicRoughnessTextureMapping .getValue (),
         textureTransformMapping,
         textureCoordinateMapping);

      if (this .occlusionTextureNode)
      {
         gl .uniform1f (shaderObject .x3d_OcclusionStrength, this .occlusionStrength);

         this .occlusionTextureNode .setNamedShaderUniforms (gl,
            shaderObject .x3d_OcclusionTexture,
            this ._occlusionTextureMapping .getValue (),
            textureTransformMapping,
            textureCoordinateMapping);
      }
   },
});

Object .defineProperties (PhysicalMaterial,
{
   ... Core_X3DNode .getStaticProperties ("PhysicalMaterial", "Shape", 2, "material", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",                        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "baseColor",                       new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "baseTextureMapping",              new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "baseTexture",                     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveColor",                   new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTextureMapping",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveTexture",                 new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metallic",                        new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "roughness",                       new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metallicRoughnessTextureMapping", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metallicRoughnessTexture",        new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionStrength",               new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionTextureMapping",         new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "occlusionTexture",                new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalScale",                     new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTextureMapping",            new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalTexture",                   new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency",                    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "extensions",                      new x_ite_Fields .MFNode ()), // experimental
      ]),
      enumerable: true,
   },
});

const PhysicalMaterial_default_ = PhysicalMaterial;
;

/* harmony default export */ const Shape_PhysicalMaterial = (Namespace/* default */.A .add ("PhysicalMaterial", PhysicalMaterial_default_));
;// ./src/x_ite/Components/Shape/TwoSidedMaterial.js









/**
 * THIS NODE IS DEPRECIATED SINCE X3D VERSION 4.0.
 */

function TwoSidedMaterial (executionContext)
{
   console .warn ("TwoSidedMaterial is deprecated, please use Appearance backMaterial field instead.");

   Shape_X3DMaterialNode .call (this, executionContext);

   this .addType (Base_X3DConstants .TwoSidedMaterial);

   // Private properties

   this .diffuseColorArray  = new Float32Array (3);
   this .specularColorArray = new Float32Array (3);
   this .emissiveColorArray = new Float32Array (3);

   this .backDiffuseColorArray  = new Float32Array (3);
   this .backSpecularColorArray = new Float32Array (3);
   this .backEmissiveColorArray = new Float32Array (3);
}

Object .assign (Object .setPrototypeOf (TwoSidedMaterial .prototype, Shape_X3DMaterialNode .prototype),
{
   initialize ()
   {
      Shape_X3DMaterialNode . prototype .initialize .call (this);

      this ._ambientIntensity .addInterest ("set_ambientIntensity__", this);
      this ._diffuseColor     .addInterest ("set_diffuseColor__",     this);
      this ._specularColor    .addInterest ("set_specularColor__",    this);
      this ._emissiveColor    .addInterest ("set_emissiveColor__",    this);
      this ._shininess        .addInterest ("set_shininess__",        this);
      this ._transparency     .addInterest ("set_transparency__",     this);

      this ._backAmbientIntensity .addInterest ("set_backAmbientIntensity__", this);
      this ._backDiffuseColor     .addInterest ("set_backDiffuseColor__",     this);
      this ._backSpecularColor    .addInterest ("set_backSpecularColor__",    this);
      this ._backEmissiveColor    .addInterest ("set_backEmissiveColor__",    this);
      this ._backShininess        .addInterest ("set_backShininess__",        this);
      this ._backTransparency     .addInterest ("set_backTransparency__",     this);

      this ._separateBackColor .addInterest ("set_transparent__", this);
      this ._transparency      .addInterest ("set_transparent__", this);
      this ._backTransparency  .addInterest ("set_transparent__", this);

      this .set_ambientIntensity__ ();
      this .set_diffuseColor__ ();
      this .set_specularColor__ ();
      this .set_emissiveColor__ ();
      this .set_shininess__ ();
      this .set_transparency__ ();

      this .set_backAmbientIntensity__ ();
      this .set_backDiffuseColor__ ();
      this .set_backSpecularColor__ ();
      this .set_backEmissiveColor__ ();
      this .set_backShininess__ ();
      this .set_backTransparency__ ();

      this .set_transparent__ ();
   },
   set_ambientIntensity__ ()
   {
      this .ambientIntensity = Math .max (this ._ambientIntensity .getValue (), 0);
   },
   set_diffuseColor__ ()
   {
      this .diffuseColorArray .set (this ._diffuseColor .getValue ());
   },
   set_specularColor__ ()
   {
      this .specularColorArray .set (this ._specularColor .getValue ());
   },
   set_emissiveColor__ ()
   {
      this .emissiveColorArray .set (this ._emissiveColor .getValue ());
   },
   set_shininess__ ()
   {
      this .shininess = Math_Algorithm .clamp (this ._shininess .getValue (), 0, 1);
   },
   set_transparency__ ()
   {
      this .transparency = Math_Algorithm .clamp (this ._transparency .getValue (), 0, 1);
   },
   /*
    * Back Material
    */
   set_backAmbientIntensity__ ()
   {
      this .backAmbientIntensity = Math .max (this ._backAmbientIntensity .getValue (), 0);
   },
   set_backDiffuseColor__ ()
   {
      this .backDiffuseColorArray .set (this ._backDiffuseColor .getValue ());
   },
   set_backSpecularColor__ ()
   {
      this .backSpecularColorArray .set (this ._backSpecularColor .getValue ());
   },
   set_backEmissiveColor__ ()
   {
      this .backEmissiveColorArray .set (this ._backEmissiveColor .getValue ());
   },
   set_backShininess__ ()
   {
      this .backShininess = Math_Algorithm .clamp (this ._backShininess .getValue (), 0, 1);
   },
   set_backTransparency__ ()
   {
      this .backTransparency = Math_Algorithm .clamp (this ._backTransparency .getValue (), 0, 1);
   },
   set_transparent__ ()
   {
      this .setTransparent (this ._transparency .getValue () || (this ._separateBackColor .getValue () && this ._backTransparency .getValue ()));
   },
   getMaterialKey: Shape_Material .prototype .getMaterialKey,
   getBaseTexture: Shape_Material .prototype .getBaseTexture,
   createShader: Shape_Material .prototype .createShader,
   setShaderUniforms (gl, shaderObject, textureTransformMapping, textureCoordinateMapping, front)
   {
      if (!front && this ._separateBackColor .getValue ())
      {
         gl .uniform1f  (shaderObject .x3d_AmbientIntensity, this .backAmbientIntensity);
         gl .uniform3fv (shaderObject .x3d_DiffuseColor,     this .backDiffuseColorArray);
         gl .uniform3fv (shaderObject .x3d_SpecularColor,    this .backSpecularColorArray);
         gl .uniform3fv (shaderObject .x3d_EmissiveColor,    this .backEmissiveColorArray);
         gl .uniform1f  (shaderObject .x3d_Shininess,        this .backShininess);
         gl .uniform1f  (shaderObject .x3d_Transparency,     this .backTransparency);
      }
      else
      {
         gl .uniform1f  (shaderObject .x3d_AmbientIntensity, this .ambientIntensity);
         gl .uniform3fv (shaderObject .x3d_DiffuseColor,     this .diffuseColorArray);
         gl .uniform3fv (shaderObject .x3d_SpecularColor,    this .specularColorArray);
         gl .uniform3fv (shaderObject .x3d_EmissiveColor,    this .emissiveColorArray);
         gl .uniform1f  (shaderObject .x3d_Shininess,        this .shininess);
         gl .uniform1f  (shaderObject .x3d_Transparency,     this .transparency);
      }
   },
});

Object .defineProperties (TwoSidedMaterial,
{
   ... Core_X3DNode .getStaticProperties ("TwoSidedMaterial", "Shape", 4, "material", "3.2"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "separateBackColor",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ambientIntensity",     new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "diffuseColor",         new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "specularColor",        new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "emissiveColor",        new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "shininess",            new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "transparency",         new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backAmbientIntensity", new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backDiffuseColor",     new x_ite_Fields .SFColor (0.8, 0.8, 0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backSpecularColor",    new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backEmissiveColor",    new x_ite_Fields .SFColor ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backShininess",        new x_ite_Fields .SFFloat (0.2)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "backTransparency",     new x_ite_Fields .SFFloat ()),
      ]),
      enumerable: true,
   },
});

const TwoSidedMaterial_default_ = TwoSidedMaterial;
;

/* harmony default export */ const Shape_TwoSidedMaterial = (Namespace/* default */.A .add ("TwoSidedMaterial", TwoSidedMaterial_default_));
;// ./src/x_ite/Components/ShapeComponent.js
















const ShapeComponent_default_ = {
   name: "Shape",
   concreteNodes:
   [
      Shape_AcousticProperties,
      Shape_Appearance,
      Shape_FillProperties,
      Shape_LineProperties,
      Shape_Material,
      Shape_PhysicalMaterial,
      Shape_PointProperties,
      Shape_Shape,
      Shape_TwoSidedMaterial,
      Shape_UnlitMaterial,
   ],
   abstractNodes:
   [
      Shape_X3DAppearanceChildNode,
      Shape_X3DAppearanceNode,
      Shape_X3DMaterialNode,
      Shape_X3DOneSidedMaterialNode,
      Shape_X3DShapeNode,
   ],
};
;

/* harmony default export */ const ShapeComponent = (Namespace/* default */.A .add ("ShapeComponent", ShapeComponent_default_));
;// ./src/x_ite/Components/Sound/X3DSoundProcessingNode.js








function X3DSoundProcessingNode (executionContext)
{
   Sound_X3DSoundNode         .call (this, executionContext);
   Time_X3DTimeDependentNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSoundProcessingNode);

   this .addChildObjects (Base_X3DConstants .inputOutput, "loop", new x_ite_Fields .SFBool ());

   const audioContext = this .getBrowser () .getAudioContext ();

   this .childNodes       = [ ];
   this .audioDestination = new GainNode (audioContext, { gain: 0 });
   this .audioSource      = new GainNode (audioContext, { gain: 1 });
   this .soundProcessor   = this .audioSource;
}

Object .assign (Object .setPrototypeOf (X3DSoundProcessingNode .prototype, Sound_X3DSoundNode .prototype),
   Time_X3DTimeDependentNode .prototype,
{
   initialize ()
   {
      Sound_X3DSoundNode         .prototype .initialize .call (this);
      Time_X3DTimeDependentNode .prototype .initialize .call (this);

      this ._gain                  .addInterest ("set_gain__",                  this);
      this ._channelCount          .addInterest ("set_channelCount__",          this);
      this ._channelCountMode      .addInterest ("set_channelCountMode__",      this);
      this ._channelInterpretation .addInterest ("set_channelInterpretation__", this);
      this ._children              .addInterest ("set_children__",              this);

      this .set_enabled__ ();
      this .set_gain__ ();
      this .set_channelCount__ ();
      this .set_channelCountMode__ ();
      this .set_channelInterpretation__ ();
      this .set_children__ ();
   },
   getAudioDestination ()
   {
      return this .audioDestination;
   },
   getAudioSource ()
   {
      return this .audioSource;
   },
   getSoundProcessor ()
   {
      return this .soundProcessor;
   },
   setSoundProcessor (value)
   {
      this .soundProcessor = value ?? this .audioSource;

      this .set_enabled__ ();
   },
   set_enabled__ ()
   {
      Time_X3DTimeDependentNode .prototype .set_enabled__ .call (this);

      this .audioDestination .disconnect ();

      if (this ._enabled .getValue ())
      {
         this .set_gain__ ();
         this .set_channelCountMode__ ();
         this .set_channelInterpretation__ ();

         this .audioDestination .connect (this .soundProcessor);
      }
      else
      {
         // If enabled field is FALSE, the audio signal passes through unmodified and is not blocked.

         this .audioDestination .gain .value           = 1;
         this .audioDestination .channelCountMode      = "max";
         this .audioDestination .channelInterpretation = "speakers";

         this .audioDestination .connect (this .audioSource);
      }
   },
   set_gain__ ()
   {
      if (!this ._enabled .getValue ())
         return;

      this .audioDestination .gain .value = this ._gain .getValue ();
   },
   set_channelCount__ ()
   {
      this .audioDestination .channelCount = Math_Algorithm .clamp (this ._channelCount .getValue (), 1, 32);
   },
   set_channelCountMode__: (() =>
   {
      const channelCountModes = new Map ([
         ["MAX",         "max"],
         ["CLAMPED-MAX", "clamped-max"],
         ["EXPLICIT",    "explicit"],
      ]);

      return function ()
      {
         if (!this ._enabled .getValue ())
            return;

         this .audioDestination .channelCountMode = channelCountModes .get (this ._channelCountMode .getValue ()) ?? "max";
      };
   })(),
   set_channelInterpretation__: (() =>
   {
      const channelInterpretations = new Map ([
         ["SPEAKERS", "speakers"],
         ["DISCRETE", "discrete"],
      ]);

      return function ()
      {
         if (!this ._enabled .getValue ())
            return;

         this .audioDestination .channelInterpretation = channelInterpretations .get (this ._channelInterpretation .getValue ()) ?? "speakers";
      };
   })(),
   set_children__ ()
   {
      for (const childNode of this .childNodes)
         childNode .getAudioSource () .disconnect (this .audioDestination);

      this .childNodes .length = 0;

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DSoundNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
               case Base_X3DConstants .X3DSoundSourceNode:
                  this .childNodes .push (childNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      for (const childNode of this .childNodes)
         childNode .getAudioSource () .connect (this .audioDestination);
   },
   set_time ()
   {
      this ._elapsedTime = this .getElapsedTime ();
   },
   dispose ()
   {
      Time_X3DTimeDependentNode .prototype .dispose .call (this);
      Sound_X3DSoundNode         .prototype .dispose .call (this);
   },
});

Object .defineProperties (X3DSoundProcessingNode, Core_X3DNode .getStaticProperties ("X3DSoundProcessingNode", "Sound", 2));

const X3DSoundProcessingNode_default_ = X3DSoundProcessingNode;
;

/* harmony default export */ const Sound_X3DSoundProcessingNode = (Namespace/* default */.A .add ("X3DSoundProcessingNode", X3DSoundProcessingNode_default_));
;// ./src/x_ite/Components/Sound/Analyser.js








function Analyser (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Analyser);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .analyserNode       = new AnalyserNode (audioContext);
   this .byteFrequencyData  = new Uint8Array (this .analyserNode .frequencyBinCount);
   this .byteTimeDomainData = new Uint8Array (this .analyserNode .frequencyBinCount);

   this .analyserNode .connect (this .getAudioSource ());
}

Object .assign (Object .setPrototypeOf (Analyser .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._fftSize               .addInterest ("set_fftSize__",               this);
      this ._minDecibels           .addInterest ("set_decibels__",              this);
      this ._maxDecibels           .addInterest ("set_decibels__",              this);
      this ._smoothingTimeConstant .addInterest ("set_smoothingTimeConstant__", this);

      this .setSoundProcessor (this .analyserNode);

      this .set_fftSize__ ();
      this .set_decibels__ ();
      this .set_smoothingTimeConstant__ ();
   },
   set_fftSize__ ()
   {
      this .analyserNode .fftSize = Math_Algorithm .clamp (Math_Algorithm .nextPowerOfTwo (this ._fftSize .getValue ()), 32, 32768);

      this ._frequencyBinCount = this .analyserNode .frequencyBinCount;
   },
   set_decibels__ ()
   {
      const
         minDecibels = Math .min (this ._minDecibels .getValue (), 0),
         maxDecibels = Math .min (this ._maxDecibels .getValue (), 0);

      this .analyserNode .minDecibels = Math .min (minDecibels, maxDecibels);
      this .analyserNode .maxDecibels = Math .max (minDecibels, maxDecibels);
   },
   set_smoothingTimeConstant__ ()
   {
      this .analyserNode .smoothingTimeConstant = Math_Algorithm .clamp (this ._smoothingTimeConstant .getValue (), 0, 1);
   },
   set_time ()
   {
      Sound_X3DSoundProcessingNode .prototype .set_time .call (this);

      const
         analyserNode      = this .analyserNode,
         frequencyBinCount = analyserNode .frequencyBinCount;

      if (this .byteFrequencyData .length !== frequencyBinCount)
      {
         this .byteFrequencyData  = new Uint8Array (frequencyBinCount);
         this .byteTimeDomainData = new Uint8Array (frequencyBinCount);
      }

      this ._byteFrequencyData   .length = frequencyBinCount;
      this ._byteTimeDomainData  .length = frequencyBinCount;
      this ._floatFrequencyData  .length = frequencyBinCount;
      this ._floatTimeDomainData .length = frequencyBinCount;

      analyserNode .getByteFrequencyData  (this .byteFrequencyData);
      analyserNode .getByteTimeDomainData (this .byteTimeDomainData);

      this ._byteFrequencyData  .getValue () .set (this .byteFrequencyData);
      this ._byteTimeDomainData .getValue () .set (this .byteTimeDomainData);

      analyserNode .getFloatFrequencyData  (this ._floatFrequencyData  .shrinkToFit ());
      analyserNode .getFloatTimeDomainData (this ._floatTimeDomainData .shrinkToFit ());

      this ._byteFrequencyData   .addEvent ();
      this ._byteTimeDomainData  .addEvent ();
      this ._floatFrequencyData  .addEvent ();
      this ._floatTimeDomainData .addEvent ();
   },
});

Object .defineProperties (Analyser,
{
   ... Core_X3DNode .getStaticProperties ("Analyser", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "fftSize",               new x_ite_Fields .SFInt32 (2048)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "minDecibels",           new x_ite_Fields .SFFloat (-100)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "maxDecibels",           new x_ite_Fields .SFFloat (-30)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "smoothingTimeConstant", new x_ite_Fields .SFFloat (0.8)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "frequencyBinCount",     new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "byteFrequencyData",     new x_ite_Fields .MFInt32 ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "byteTimeDomainData",    new x_ite_Fields .MFInt32 ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "floatFrequencyData",    new x_ite_Fields .MFFloat ()), // experimental
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "floatTimeDomainData",   new x_ite_Fields .MFFloat ()), // experimental

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Analyser_default_ = Analyser;
;

/* harmony default export */ const Sound_Analyser = (Namespace/* default */.A .add ("Analyser", Analyser_default_));
;// ./src/x_ite/Components/Sound/X3DSoundSourceNode.js





function X3DSoundSourceNode (executionContext)
{
   Sound_X3DSoundNode         .call (this, executionContext);
   Time_X3DTimeDependentNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSoundSourceNode);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .audioSource  = new GainNode (audioContext, { gain: 0 });
   this .mediaElement = null;
}

Object .assign (Object .setPrototypeOf (X3DSoundSourceNode .prototype, Sound_X3DSoundNode .prototype),
   Time_X3DTimeDependentNode .prototype,
{
   initialize ()
   {
      Sound_X3DSoundNode         .prototype .initialize .call (this);
      Time_X3DTimeDependentNode .prototype .initialize .call (this);

      this ._gain .addInterest ("set_gain__", this);

      this .set_gain__ ();
   },
   getAudioSource ()
   {
      return this .audioSource;
   },
   getMediaElement ()
   {
      return this .mediaElement;
   },
   setMediaElement (value)
   {
      this .getBrowser () .stopAudioElement (this .mediaElement);

      this .mediaElement = value;

      if (!value)
         return;

      // Init mediaElement.

      this .set_loop ();

      // Handle events.

      this .resetElapsedTime ();

      if (this ._isActive .getValue ())
      {
         if (this ._isPaused .getValue ())
         {
            this .set_pause ();
         }
         else
         {
            if (this .getLive () .getValue ())
               this .set_start ();
            else
               this .set_pause ();
         }
      }
      else
      {
         this .set_stop ();
      }
   },
   set_gain__ ()
   {
      this .audioSource .gain .value = this ._gain .getValue ();
   },
   set_loop ()
   {
      if (!this .mediaElement)
         return;

      this .mediaElement .loop = this ._loop .getValue ();
   },
   set_start ()
   {
      if (!this .mediaElement)
         return;

      this .mediaElement .currentTime = 0;

      this .getBrowser () .startAudioElement (this .mediaElement);
   },
   set_pause ()
   {
      this .getBrowser () .stopAudioElement (this .mediaElement);
   },
   set_resume ()
   {
      this .getBrowser () .startAudioElement (this .mediaElement);
   },
   set_stop ()
   {
      this .getBrowser () .stopAudioElement (this .mediaElement);
   },
   set_end ()
   {
      if (this ._loop .getValue ())
         return;

      this .stop ();
   },
   set_time ()
   {
      if (!this .mediaElement)
         return;

      this ._elapsedTime = this .getElapsedTime ();

      if (this .mediaElement .currentTime < this .mediaElement .duration)
         return;

      this .set_end ();
   },
   dispose ()
   {
      Time_X3DTimeDependentNode .prototype .dispose .call (this);
      Sound_X3DSoundNode         .prototype .dispose .call (this);
   },
});

Object .defineProperties (X3DSoundSourceNode, Core_X3DNode .getStaticProperties ("X3DSoundSourceNode", "Sound", 1));

const X3DSoundSourceNode_default_ = X3DSoundSourceNode;
;

/* harmony default export */ const Sound_X3DSoundSourceNode = (Namespace/* default */.A .add ("X3DSoundSourceNode", X3DSoundSourceNode_default_));
;// ./src/x_ite/Browser/Sound/AudioElement.js

const AudioElement_default_ = {
   create (audioContext, audioSource, audioBuffer)
   {
      let
         audioBufferSource = new AudioBufferSourceNode (audioContext),
         detune            = 0,
         playbackRate      = 1,
         loopStart         = 0,
         loopEnd           = 0,
         loop              = false,
         startTime         = 0,
         currentTime       = 0,
         active            = false;

      return Object .defineProperties ({ },
      {
         detune:
         {
            get ()
            {
               return detune;
            },
            set (value)
            {
               detune                           = value;
               audioBufferSource .detune .value = value;
            },
         },
         playbackRate:
         {
            get ()
            {
               return playbackRate;
            },
            set (value)
            {
               playbackRate                           = value;
               audioBufferSource .playbackRate .value = value;
            },
         },
         loopStart:
         {
            get ()
            {
               return loopStart;
            },
            set (value)
            {
               loopStart                    = value;
               audioBufferSource .loopStart = value;
            },
         },
         loopEnd:
         {
            get ()
            {
               return loopEnd;
            },
            set (value)
            {
               loopEnd                    = value;
               audioBufferSource .loopEnd = value;
            },
         },
         loop:
         {
            get ()
            {
               return loop;
            },
            set (value)
            {
               if (!value)
                  startTime = audioContext .currentTime - this .currentTime;

               loop                    = value;
               audioBufferSource .loop = value;
            },
         },
         currentTime:
         {
            get ()
            {
               if (active)
               {
                  if (this .duration)
                  {
                     if (this .loop)
                        return (audioContext .currentTime - startTime) % this .duration;

                     return audioContext .currentTime - startTime;
                  }

                  return 0;
               }

               return currentTime;
            },
            set (value)
            {
               currentTime = value;
               startTime   = audioContext .currentTime - currentTime;

               if (!active)
                  return;

               this .pause ();
               this .play ();
            },
         },
         duration:
         {
            value: audioBuffer ?.duration ?? 0,
         },
         play:
         {
            value ()
            {
               if (active)
                  return Promise .resolve ();

               audioBufferSource = new AudioBufferSourceNode (audioContext);

               audioBufferSource .buffer              = audioBuffer;
               audioBufferSource .loopStart           = loopStart;
               audioBufferSource .loopEnd             = loopEnd;
               audioBufferSource .loop                = loop;
               audioBufferSource .playbackRate .value = playbackRate;

               audioBufferSource .connect (audioSource);
               audioBufferSource .start (0, currentTime);

               startTime = audioContext .currentTime - currentTime;
               active    = true;

               return Promise .resolve ();
            },
         },
         pause:
         {
            value ()
            {
               if (!active)
                  return;

               audioBufferSource .stop ();
               audioBufferSource .disconnect ();

               currentTime = this .currentTime;
               active      = false;

               // Create 1s silence to clear channels.

               const silence = new AudioBufferSourceNode (audioContext);

               silence .connect (audioSource);
               silence .start ();
               silence .stop (audioContext .currentTime + 1);
            },
         },
      });
   },
};
;

/* harmony default export */ const AudioElement = (Namespace/* default */.A .add ("AudioElement", AudioElement_default_));
;// ./src/x_ite/Components/Sound/AudioClip.js











function AudioClip (executionContext)
{
   Sound_X3DSoundSourceNode .call (this, executionContext);
   Networking_X3DUrlObject       .call (this, executionContext);

   this .addType (Base_X3DConstants .AudioClip);
}

Object .assign (Object .setPrototypeOf (AudioClip .prototype, Sound_X3DSoundSourceNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);
      Networking_X3DUrlObject       .prototype .initialize .call (this);

      this ._pitch .addInterest ("set_pitch__", this);

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_live__ ()
   {
      Sound_X3DSoundSourceNode .prototype .set_live__ .call (this);
      Networking_X3DUrlObject       .prototype .set_live__ .call (this);
   },
   set_pitch__ ()
   {
      const media = this .getMediaElement ();

      if (media)
         media .playbackRate = Math .max (this ._pitch .getValue (), 0);
   },
   unloadData ()
   {
      this .setMediaElement (null);
   },
   loadData ()
   {
      this .unloadData ();

      new InputOutput_FileLoader (this, { dataAsString: false }) .loadDocument (this ._url, async (data, fileURL) =>
      {
         if (data === null)
         {
            this .unloadData ();
            this .setLoadState (Base_X3DConstants .FAILED_STATE);
         }
         else if (data instanceof ArrayBuffer)
         {
            await this .setArrayBuffer (data);

            this .setLoadState (Base_X3DConstants .COMPLETE_STATE);

            if (DEVELOPMENT)
            {
               if (fileURL .protocol !== "data:")
                  console .info (`Done loading audio '${decodeURI (fileURL)}'.`);
            }
         }
         else
         {
            throw new Error ("AudioClip: no suitable file type handler found.");
         }
      });
   },
   async setArrayBuffer (arrayBuffer)
   {
      const
         audioContext = this .getBrowser () .getAudioContext (),
         audioBuffer  = await audioContext .decodeAudioData (arrayBuffer),
         media        = AudioElement .create (audioContext, this .getAudioSource (), audioBuffer);

      this ._duration_changed = media .duration;

      this .setMediaElement (media);

      this .set_pitch__ ();
   },
   dispose ()
   {
      Networking_X3DUrlObject       .prototype .dispose .call (this);
      Sound_X3DSoundSourceNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (AudioClip,
{
   ... Core_X3DNode .getStaticProperties ("AudioClip", "Sound", 1, "source", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                 new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pitch",                new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "loop",                 new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",           new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",          new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "duration_changed",     new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const AudioClip_default_ = AudioClip;
;

/* harmony default export */ const Sound_AudioClip = (Namespace/* default */.A .add ("AudioClip", AudioClip_default_));
;// ./src/x_ite/Components/Sound/X3DSoundDestinationNode.js






function X3DSoundDestinationNode (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSoundDestinationNode);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .childNodes       = [ ];
   this .audioDestination = new GainNode (audioContext, { gain: 0 });
}

Object .assign (Object .setPrototypeOf (X3DSoundDestinationNode .prototype, Sound_X3DSoundNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundNode .prototype .initialize .call (this);

      this .getLive () .addInterest ("set_enabled__", this);

      this ._enabled               .addInterest ("set_enabled__",               this);
      this ._gain                  .addInterest ("set_gain__",                  this);
      this ._channelCount          .addInterest ("set_channelCount__",          this);
      this ._channelCountMode      .addInterest ("set_channelCountMode__",      this);
      this ._channelInterpretation .addInterest ("set_channelInterpretation__", this);
      this ._children              .addInterest ("set_children__",              this);

      this .set_enabled__ ();
      this .set_gain__ ();
      this .set_channelCount__ ();
      this .set_channelCountMode__ ();
      this .set_channelInterpretation__ ();
      this .set_children__ ();
   },
   getAudioDestination ()
   {
      return this .audioDestination;
   },
   set_enabled__ ()
   {
      // If enabled field is FALSE, the audio signal is blocked and does not pass through.

      if (this ._enabled .getValue () && this .getLive () .getValue ())
         this .audioDestination .connect (this .getSoundDestination ());
      else
         this .audioDestination .disconnect ();

      this ._isActive = this ._enabled;
   },
   set_gain__ ()
   {
      this .audioDestination .gain .value = this ._gain .getValue ();
   },
   set_channelCount__ ()
   {
      this .audioDestination .channelCount = Math_Algorithm .clamp (this ._channelCount .getValue (), 1, 32);
   },
   set_channelCountMode__: (() =>
   {
      const channelCountModes = new Map ([
         ["MAX",         "max"],
         ["CLAMPED-MAX", "clamped-max"],
         ["EXPLICIT",    "explicit"],
      ]);

      return function ()
      {
         this .audioDestination .channelCountMode = channelCountModes .get (this ._channelCountMode .getValue ()) ?? "max";
      };
   })(),
   set_channelInterpretation__: (() =>
   {
      const channelInterpretations = new Map ([
         ["SPEAKERS", "speakers"],
         ["DISCRETE", "discrete"],
      ]);

      return function ()
      {
         this .audioDestination .channelInterpretation = channelInterpretations .get (this ._channelInterpretation .getValue ()) ?? "speakers";
      };
   })(),
   set_children__ ()
   {
      for (const childNode of this .childNodes)
         childNode .getAudioSource () .disconnect (this .audioDestination);

      this .childNodes .length = 0;

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
               case Base_X3DConstants .X3DSoundSourceNode:
                  this .childNodes .push (childNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      for (const childNode of this .childNodes)
         childNode .getAudioSource () .connect (this .audioDestination);
   },
});

Object .defineProperties (X3DSoundDestinationNode, Core_X3DNode .getStaticProperties ("X3DSoundDestinationNode", "Sound", 2));

const X3DSoundDestinationNode_default_ = X3DSoundDestinationNode;
;

/* harmony default export */ const Sound_X3DSoundDestinationNode = (Namespace/* default */.A .add ("X3DSoundDestinationNode", X3DSoundDestinationNode_default_));
;// ./src/x_ite/Components/Sound/AudioDestination.js







function AudioDestination (executionContext)
{
   Sound_X3DSoundDestinationNode .call (this, executionContext);

   this .addType (Base_X3DConstants .AudioDestination);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .audioElement                    = new Audio ();
   this .mediaStreamAudioDestinationNode = new MediaStreamAudioDestinationNode (audioContext);
   this .audioElement .srcObject         = this .mediaStreamAudioDestinationNode .stream;
}

Object .assign (Object .setPrototypeOf (AudioDestination .prototype, Sound_X3DSoundDestinationNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundDestinationNode .prototype .initialize .call (this);

      const audioContext = this .getBrowser () .getAudioContext ();

      this ._mediaDeviceID .addInterest ("set_mediaDeviceID__", this);

      this ._maxChannelCount = audioContext .destination .maxChannelCount;

      this .set_mediaDeviceID__ ();
   },
   getSoundDestination ()
   {
      return this .mediaStreamAudioDestinationNode;
   },
   set_enabled__ ()
   {
      const
         browser = this .getBrowser (),
         active  = this ._enabled .getValue () && this .getLive () .getValue ();

      if (active)
         browser .startAudioElement (this .audioElement);
      else
         browser .stopAudioElement (this .audioElement);

      Sound_X3DSoundDestinationNode .prototype .set_enabled__ .call (this);
   },
   set_mediaDeviceID__ ()
   {
      this .getBrowser () .startAudioElement (this, "set_mediaDeviceID_impl__");
   },
   set_mediaDeviceID_impl__ ()
   {
      const sinkId = this ._mediaDeviceID .getValue () || "default";

      return this .audioElement .setSinkId ?.(sinkId) ?? Promise .resolve ();
   },
});

Object .defineProperties (AudioDestination,
{
   ... Core_X3DNode .getStaticProperties ("AudioDestination", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mediaDeviceID",         new x_ite_Fields .SFString ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "maxChannelCount",       new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const AudioDestination_default_ = AudioDestination;
;

/* harmony default export */ const Sound_AudioDestination = (Namespace/* default */.A .add ("AudioDestination", AudioDestination_default_));
;// ./src/x_ite/Components/Sound/BiquadFilter.js








function BiquadFilter (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .BiquadFilter);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .biquadFilterNode = new BiquadFilterNode (audioContext);

   this .biquadFilterNode .connect (this .getAudioSource ());
}

Object .assign (Object .setPrototypeOf (BiquadFilter .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._detune        .addInterest ("set_detune__",        this);
      this ._type          .addInterest ("set_type__",          this);
      this ._frequency     .addInterest ("set_frequency__",     this);
      this ._qualityFactor .addInterest ("set_qualityFactor__", this);

      this .setSoundProcessor (this .biquadFilterNode);

      this .set_detune__ ();
      this .set_type__ ();
      this .set_frequency__ ();
      this .set_qualityFactor__ ();
   },
   set_detune__ ()
   {
      this .biquadFilterNode .detune .value = this ._detune .getValue ();
   },
   set_type__: (() =>
   {
      const types = new Map ([
         ["LOWPASS",   "lowpass"],
         ["HIGHPASS",  "highpass"],
         ["BANDPASS",  "bandpass"],
         ["LOWSHELF",  "lowshelf"],
         ["HIGHSHELF", "highshelf"],
         ["PEAKING",   "peaking"],
         ["NOTCH",     "notch"],
         ["ALLPASS",   "allpass"],
      ]);

      return function ()
      {
         this .biquadFilterNode .type = types .get (this ._type .getValue ()) ?? "lowpass";
      };
   })(),
   set_frequency__ ()
   {
      this .biquadFilterNode .frequency .value = Math .max (this ._frequency .getValue (), 0);
   },
   set_qualityFactor__ ()
   {
      this .biquadFilterNode .Q .value = Math_Algorithm .clamp (this ._qualityFactor .getValue (), 0.0001, 1000);
   },
});

Object .defineProperties (BiquadFilter,
{
   ... Core_X3DNode .getStaticProperties ("BiquadFilter", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "detune",                new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "type",                  new x_ite_Fields .SFString ("LOWPASS")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "frequency",             new x_ite_Fields .SFFloat (350)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "qualityFactor",         new x_ite_Fields .SFFloat (1)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const BiquadFilter_default_ = BiquadFilter;
;

/* harmony default export */ const Sound_BiquadFilter = (Namespace/* default */.A .add ("BiquadFilter", BiquadFilter_default_));
;// ./src/x_ite/Components/Sound/BufferAudioSource.js












function BufferAudioSource (executionContext)
{
   Sound_X3DSoundSourceNode .call (this, executionContext);
   Networking_X3DUrlObject       .call (this, executionContext);

   this .addType (Base_X3DConstants .BufferAudioSource);
}

Object .assign (Object .setPrototypeOf (BufferAudioSource .prototype, Sound_X3DSoundSourceNode .prototype),
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);
      Networking_X3DUrlObject       .prototype .initialize .call (this);

      this ._numberOfChannels .addInterest ("set_buffer__",       this);
      this ._sampleRate       .addInterest ("set_buffer__",       this);
      this ._bufferLength     .addInterest ("set_buffer__",       this);
      this ._buffer           .addInterest ("set_buffer__",       this);
      this ._detune           .addInterest ("set_detune__",       this);
      this ._playbackRate     .addInterest ("set_playbackRate__", this);
      this ._loopStart        .addInterest ("set_loopStart__",    this);
      this ._loopEnd          .addInterest ("set_loopEnd__",      this);

      this .set_buffer__ ();

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_live__ ()
   {
      Sound_X3DSoundSourceNode .prototype .set_live__ .call (this);
      Networking_X3DUrlObject       .prototype .set_live__ .call (this);
   },
   set_buffer__ ()
   {
      if (this ._load .getValue ())
         return;

      const
         audioContext     = this .getBrowser () .getAudioContext (),
         numberOfChannels = Math_Algorithm .clamp (this ._numberOfChannels .getValue (), 1, 32),
         sampleRate       = Math_Algorithm .clamp (this ._sampleRate .getValue (), 3000, 768000),
         bufferLength     = Math .max (this ._bufferLength .getValue (), 0);

      if (bufferLength)
      {
         const
            audioBuffer = audioContext .createBuffer (numberOfChannels, bufferLength, sampleRate),
            buffer      = this ._buffer .getValue ();

         this ._bufferDuration = bufferLength / sampleRate;

         if (this ._buffer .length >= bufferLength * numberOfChannels)
         {
            for (let i = 0; i < numberOfChannels; ++ i)
            {
               const channelData = audioBuffer .getChannelData (i);

               channelData .set (buffer .subarray (i * bufferLength, (i + 1) * bufferLength));
            }
         }

         this .setMediaElement (AudioElement .create (audioContext, this .getAudioSource (), audioBuffer));

         this .set_detune__ ();
         this .set_playbackRate__ ();
         this .set_loopStart__ ();
         this .set_loopEnd__ ();
      }
      else
      {
         this ._bufferDuration = 0;

         this .setMediaElement (null);
      }
   },
   set_detune__ ()
   {
      const media = this .getMediaElement ();

      if (media)
         media .detune = this ._detune .getValue ();
   },
   set_playbackRate__ ()
   {
      const media = this .getMediaElement ();

      if (media)
         media .playbackRate = this ._playbackRate .getValue ();
   },
   set_loopStart__ ()
   {
      const media = this .getMediaElement ();

      if (media)
         media .loopStart = Math .max (this ._loopStart .getValue (), 0);
   },
   set_loopEnd__ ()
   {
      const media = this .getMediaElement ();

      if (media)
         media .loopEnd = Math .max (this ._loopEnd .getValue (), 0);
   },
   unloadData ()
   {
      this .set_buffer__ ();
   },
   loadData ()
   {
      this .setMediaElement (null);

      new InputOutput_FileLoader (this) .loadDocument (this ._url, async (data, URL) =>
      {
         if (data === null)
         {
            // No URL could be loaded.
            this .setMediaElement (null);
            this .setLoadState (Base_X3DConstants .FAILED_STATE);
         }
         else if (data instanceof ArrayBuffer)
         {
            await this .setArrayBuffer (data);

            this .setLoadState (Base_X3DConstants .COMPLETE_STATE);

            if (DEVELOPMENT)
            {
               if (URL .protocol !== "data:")
                  console .info (`Done loading audio '${decodeURI (URL)}'.`);
            }
         }
      });
   },
   async setArrayBuffer (arrayBuffer)
   {
      const
         audioContext = this .getBrowser () .getAudioContext (),
         audioBuffer  = await audioContext .decodeAudioData (arrayBuffer),
         bufferData   = new Float32Array (audioBuffer .length * audioBuffer .numberOfChannels);

      for (let i = 0; i < audioBuffer .numberOfChannels; ++ i)
         bufferData .set (audioBuffer .getChannelData (i), i * audioBuffer .length);

      this ._numberOfChannels = audioBuffer .numberOfChannels;
      this ._sampleRate       = audioBuffer .sampleRate;
      this ._bufferLength     = audioBuffer .length;
      this ._buffer           = bufferData;
      this ._bufferDuration   = audioBuffer .duration;

      this .setMediaElement (AudioElement .create (audioContext, this .getAudioSource (), audioBuffer));

      this .set_detune__ ();
      this .set_playbackRate__ ();
      this .set_loopStart__ ();
      this .set_loopEnd__ ();
   },
   dispose ()
   {
      Networking_X3DUrlObject       .prototype .dispose .call (this);
      Sound_X3DSoundSourceNode .prototype .dispose .call (this);
   },
});

Object .defineProperties (BufferAudioSource,
{
   ... Core_X3DNode .getStaticProperties ("BufferAudioSource", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "load",                  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "url",                   new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefresh",           new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "autoRefreshTimeLimit",  new x_ite_Fields .SFTime (3600)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "numberOfChannels",      new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "sampleRate",            new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "bufferLength",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "buffer",                new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "bufferDuration",        new x_ite_Fields .SFTime ()),  // skip test

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "detune",                new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "playbackRate",          new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "loopStart",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "loopEnd",               new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "loop",                  new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const BufferAudioSource_default_ = BufferAudioSource;
;

/* harmony default export */ const Sound_BufferAudioSource = (Namespace/* default */.A .add ("BufferAudioSource", BufferAudioSource_default_));
;// ./src/x_ite/Components/Sound/X3DSoundChannelNode.js






function X3DSoundChannelNode (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .X3DSoundChannelNode);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .childNodes       = [ ];
   this .audioDestination = new GainNode (audioContext, { gain: 0 });
   this .audioSource      = new GainNode (audioContext, { gain: 1 });
   this .soundProcessor   = this .audioSource;
}

Object .assign (Object .setPrototypeOf (X3DSoundChannelNode .prototype, Sound_X3DSoundNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundNode .prototype .initialize .call (this);

      this ._enabled               .addInterest ("set_enabled__",               this);
      this ._gain                  .addInterest ("set_gain__",                  this);
      this ._channelCount          .addInterest ("set_channelCount__",          this);
      this ._channelCountMode      .addInterest ("set_channelCountMode__",      this);
      this ._channelInterpretation .addInterest ("set_channelInterpretation__", this);
      this ._children              .addInterest ("set_children__",              this);

      this .set_enabled__ ();
      this .set_gain__ ();
      this .set_channelCount__ ();
      this .set_channelCountMode__ ();
      this .set_channelInterpretation__ ();
      this .set_children__ ();
   },
   getAudioDestination ()
   {
      return this .audioDestination;
   },
   getAudioSource ()
   {
      return this .audioSource;
   },
   getSoundProcessor ()
   {
      return this .soundProcessor;
   },
   setSoundProcessor (value)
   {
      this .soundProcessor = value ?? this .audioSource;

      this .set_enabled__ ();
   },
   set_enabled__ ()
   {
      this .audioDestination .disconnect ();

      if (this ._enabled .getValue ())
      {
         this .set_gain__ ();
         this .set_channelCountMode__ ();
         this .set_channelInterpretation__ ();

         this .audioDestination .connect (this .soundProcessor);
      }
      else
      {
         // If enabled field is FALSE, the audio signal passes through unmodified and is not blocked.

         this .audioDestination .gain .value           = 1;
         this .audioDestination .channelCountMode      = "max";
         this .audioDestination .channelInterpretation = "speakers";

         this .audioDestination .connect (this .audioSource);
      }
   },
   set_gain__ ()
   {
      if (!this ._enabled .getValue ())
         return;

      this .audioDestination .gain .value = this ._gain .getValue ();
   },
   set_channelCount__ ()
   {
      this .audioDestination .channelCount = Math_Algorithm .clamp (this ._channelCount .getValue (), 1, 32);
   },
   set_channelCountMode__: (() =>
   {
      const channelCountModes = new Map ([
         ["MAX",         "max"],
         ["CLAMPED-MAX", "clamped-max"],
         ["EXPLICIT",    "explicit"],
      ]);

      return function ()
      {
         if (!this ._enabled .getValue ())
            return;

         this .audioDestination .channelCountMode = channelCountModes .get (this ._channelCountMode .getValue ()) ?? "max";
      };
   })(),
   set_channelInterpretation__: (() =>
   {
      const channelInterpretations = new Map ([
         ["SPEAKERS", "speakers"],
         ["DISCRETE", "discrete"],
      ]);

      return function ()
      {
         if (!this ._enabled .getValue ())
            return;

         this .audioDestination .channelInterpretation = channelInterpretations .get (this ._channelInterpretation .getValue ()) ?? "speakers";
      };
   })(),
   set_children__ ()
   {
      for (const [i, childNode] of this .childNodes .entries ())
         this .disconnectChildNode (i, childNode);

      this .childNodes .length = 0;

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
               case Base_X3DConstants .X3DSoundSourceNode:
                  this .childNodes .push (childNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      this .setChildNodes (this .childNodes);

      for (const [i, childNode] of this .childNodes .entries ())
         this .connectChildNode (i, childNode);
   },
   setChildNodes (childNodes)
   { },
   connectChildNode (i, childNode)
   {
      childNode .getAudioSource () .connect (this .audioDestination);
   },
   disconnectChildNode (i, childNode)
   {
      childNode .getAudioSource () .disconnect (this .audioDestination);
   },
});

Object .defineProperties (X3DSoundChannelNode, Core_X3DNode .getStaticProperties ("X3DSoundChannelNode", "Sound", 2));

const X3DSoundChannelNode_default_ = X3DSoundChannelNode;
;

/* harmony default export */ const Sound_X3DSoundChannelNode = (Namespace/* default */.A .add ("X3DSoundChannelNode", X3DSoundChannelNode_default_));
;// ./src/x_ite/Components/Sound/ChannelMerger.js








function ChannelMerger (executionContext)
{
   Sound_X3DSoundChannelNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ChannelMerger);
}

Object .assign (Object .setPrototypeOf (ChannelMerger .prototype, Sound_X3DSoundChannelNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundChannelNode .prototype .initialize .call (this);

      this ._enabled .addInterest ("set_children__", this);
   },
   setChildNodes (childNodes)
   {
      this .channelMergerNode ?.disconnect ();

      if (this ._enabled .getValue ())
      {
         const
            audioContext   = this .getBrowser () .getAudioContext (),
            numberOfInputs = Math_Algorithm .clamp (childNodes .length, 1, 32);

         if (this .channelMergerNode ?.numberOfInputs !== numberOfInputs)
            this .channelMergerNode = new ChannelMergerNode (audioContext, { numberOfInputs });

         this .channelMergerNode .connect (this .getAudioDestination ());
      }
      else
      {
         this .channelMergerNode = null;
      }
   },
   connectChildNode (i, childNode)
   {
      if (this .channelMergerNode)
      {
         if (i < 32)
            childNode .getAudioSource () .connect (this .channelMergerNode, 0, i);
      }
      else
      {
         childNode .getAudioSource () .connect (this .getAudioDestination ());
      }
   },
   disconnectChildNode (i, childNode)
   {
      if (this .channelMergerNode)
      {
         if (i < 32)
            childNode .getAudioSource () .disconnect (this .channelMergerNode, 0, i);
      }
      else
      {
         childNode .getAudioSource () .disconnect (this .getAudioDestination ());
      }
   },
});

Object .defineProperties (ChannelMerger,
{
   ... Core_X3DNode .getStaticProperties ("ChannelMerger", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ChannelMerger_default_ = ChannelMerger;
;

/* harmony default export */ const Sound_ChannelMerger = (Namespace/* default */.A .add ("ChannelMerger", ChannelMerger_default_));
;// ./src/x_ite/Components/Sound/ChannelSelector.js








function ChannelSelector (executionContext)
{
   Sound_X3DSoundChannelNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ChannelSelector);
}

Object .assign (Object .setPrototypeOf (ChannelSelector .prototype, Sound_X3DSoundChannelNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundChannelNode .prototype .initialize .call (this);

      this ._channelSelection .addInterest ("set_channelSelection__", this);

      this .set_channelSelection__ ();
   },
   set_channelSelection__ ()
   {
      const
         audioContext     = this .getBrowser () .getAudioContext (),
         channelSelection = Math_Algorithm .clamp (this ._channelSelection .getValue (), 0, 31),
         numberOfOutputs  = channelSelection + 1;

      this .channelSplitterNode ?.disconnect ();

      if (this .channelSplitterNode ?.numberOfOutputs !== numberOfOutputs)
         this .channelSplitterNode = new ChannelSplitterNode (audioContext, { numberOfOutputs });

      this .channelSplitterNode .connect (this .getAudioSource (), channelSelection);

      this .setSoundProcessor (this .channelSplitterNode);
   },
});

Object .defineProperties (ChannelSelector,
{
   ... Core_X3DNode .getStaticProperties ("ChannelSelector", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelSelection",      new x_ite_Fields .SFInt32 ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ChannelSelector_default_ = ChannelSelector;
;

/* harmony default export */ const Sound_ChannelSelector = (Namespace/* default */.A .add ("ChannelSelector", ChannelSelector_default_));
;// ./src/x_ite/Components/Sound/ChannelSplitter.js









function ChannelSplitter (executionContext)
{
   Sound_X3DSoundChannelNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ChannelSplitter);

   this .outputNodes = [ ];
}

Object .assign (Object .setPrototypeOf (ChannelSplitter .prototype, Sound_X3DSoundChannelNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundChannelNode .prototype .initialize .call (this);

      this ._enabled .addInterest ("set_outputs__", this);
      this ._outputs .addInterest ("set_outputs__", this);

      this .set_outputs__ ();
   },
   set_outputs__ ()
   {
      if (this .channelSplitterNode)
      {
         this .channelSplitterNode .disconnect ();
      }
      else
      {
         for (const outputNode of this .outputNodes)
            this .getAudioSource () .disconnect (outputNode .getAudioDestination ());
      }

      this .outputNodes .length = 0;

      for (const child of this ._outputs)
      {
         const outputNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!outputNode)
            continue;

         const type = outputNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
                  this .outputNodes .push (outputNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      if (this .channelSplitterNode)
         this .getAudioSource () .disconnect (this .channelSplitterNode);

      if (this ._enabled .getValue ())
      {
         const
            audioContext    = this .getBrowser () .getAudioContext (),
            numberOfOutputs = Math_Algorithm .clamp (this .outputNodes .length, 1, 32);

         if (this .channelSplitterNode ?.numberOfOutputs !== numberOfOutputs)
            this .channelSplitterNode = new ChannelSplitterNode (audioContext, { numberOfOutputs });

         this .getAudioSource () .connect (this .channelSplitterNode);
      }
      else
      {
         this .channelSplitterNode = null;
      }

      if (this .channelSplitterNode)
      {
         const length = Math .min (this .outputNodes .length, 32);

         for (let i = 0; i < length; ++ i)
            this .channelSplitterNode .connect (this .outputNodes [i] .getAudioDestination (), i);
      }
      else
      {
         for (const outputNode of this .outputNodes)
            this .getAudioSource () .connect (outputNode .getAudioDestination ());
      }
   },
});

Object .defineProperties (ChannelSplitter,
{
   ... Core_X3DNode .getStaticProperties ("ChannelSplitter", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "outputs",               new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const ChannelSplitter_default_ = ChannelSplitter;
;

/* harmony default export */ const Sound_ChannelSplitter = (Namespace/* default */.A .add ("ChannelSplitter", ChannelSplitter_default_));
;// ./src/x_ite/Components/Sound/Convolver.js








function Convolver (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Convolver);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .convolverNode = new ConvolverNode (audioContext);

   this .convolverNode .connect (this .getAudioSource ());
}

Object .assign (Object .setPrototypeOf (Convolver .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._numberOfChannels .addInterest ("set_buffer__",    this);
      this ._buffer           .addInterest ("set_buffer__",    this);
      this ._normalize        .addInterest ("set_normalize__", this);

      this .setSoundProcessor (this .convolverNode);

      this .set_buffer__ ();
      this .set_normalize__ ();
   },
   set_buffer__ ()
   {
      const
         audioContext     = this .getBrowser () .getAudioContext (),
         numberOfChannels = Math_Algorithm .clamp (this ._numberOfChannels .getValue (), 1, 32),
         sampleRate       = audioContext .sampleRate,
         bufferLength     = Math .max (Math .floor (this ._buffer .length / numberOfChannels), 1),
         audioBuffer      = audioContext .createBuffer (numberOfChannels, bufferLength, sampleRate),
         buffer           = this ._buffer .getValue ();

      if (this ._buffer .length >= bufferLength * numberOfChannels)
      {
         for (let i = 0; i < numberOfChannels; ++ i)
         {
            const channelData = audioBuffer .getChannelData (i);

            channelData .set (buffer .subarray (i * bufferLength, (i + 1) * bufferLength));
         }
      }

      this .convolverNode .buffer = audioBuffer;
   },
   set_normalize__ ()
   {
      this .convolverNode .normalize = this ._normalize .getValue ();
   },
});

Object .defineProperties (Convolver,
{
   ... Core_X3DNode .getStaticProperties ("Convolver", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "numberOfChannels",      new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "buffer",                new x_ite_Fields .MFFloat ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "normalize",             new x_ite_Fields .SFBool ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Convolver_default_ = Convolver;
;

/* harmony default export */ const Sound_Convolver = (Namespace/* default */.A .add ("Convolver", Convolver_default_));
;// ./src/x_ite/Components/Sound/Delay.js








function Delay (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Delay);
}

Object .assign (Object .setPrototypeOf (Delay .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._maxDelayTime .addInterest ("set_maxDelayTime__", this);
      this ._delayTime    .addInterest ("set_delayTime__",    this);

      this .set_maxDelayTime__ ();
   },
   set_delayTime__ ()
   {
      const
         maxDelayTime = Math .max (this ._maxDelayTime .getValue (), 0),
         delayTime    = Math_Algorithm .clamp (this ._delayTime .getValue (), 0, maxDelayTime);

      this .delayNode .delayTime .value = delayTime;
   },
   set_maxDelayTime__ ()
   {
      this .delayNode ?.disconnect ();

      this .delayNode = this .createSoundProcessor ();

      this .delayNode .connect (this .getAudioSource ());

      this .setSoundProcessor (this .delayNode);
   },
   createSoundProcessor ()
   {
      const
         audioContext = this .getBrowser () .getAudioContext (),
         maxDelayTime = Math .max (this ._maxDelayTime .getValue (), 0),
         delayTime    = Math_Algorithm .clamp (this ._delayTime .getValue (), 0, maxDelayTime),
         delayNode    = new DelayNode (audioContext, { maxDelayTime, delayTime });

      return delayNode;
   },
});

Object .defineProperties (Delay,
{
   ... Core_X3DNode .getStaticProperties ("Delay", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "maxDelayTime",          new x_ite_Fields .SFTime (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "delayTime",             new x_ite_Fields .SFTime (0)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Delay_default_ = Delay;
;

/* harmony default export */ const Sound_Delay = (Namespace/* default */.A .add ("Delay", Delay_default_));
;// ./src/x_ite/Components/Sound/DynamicsCompressor.js








function DynamicsCompressor (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .DynamicsCompressor);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .dynamicsCompressorNode = new DynamicsCompressorNode (audioContext);

   this .dynamicsCompressorNode .connect (this .getAudioSource ());
}

Object .assign (Object .setPrototypeOf (DynamicsCompressor .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._attack    .addInterest ("set_attack__",    this);
      this ._knee      .addInterest ("set_knee__",      this);
      this ._ratio     .addInterest ("set_ratio__",     this);
      this ._release   .addInterest ("set_release__",   this);
      this ._threshold .addInterest ("set_threshold__", this);

      this .setSoundProcessor (this .dynamicsCompressorNode);

      this .set_attack__ ();
      this .set_knee__ ();
      this .set_ratio__ ();
      this .set_release__ ();
      this .set_threshold__ ();
   },
   set_attack__ ()
   {
      this .dynamicsCompressorNode .attack .value = Math_Algorithm .clamp (this ._attack .getValue (), 0, 1);
   },
   set_knee__ ()
   {
      this .dynamicsCompressorNode .knee .value = Math_Algorithm .clamp (this ._knee .getValue (), 0, 40);
   },
   set_ratio__ ()
   {
      this .dynamicsCompressorNode .ratio .value = Math_Algorithm .clamp (this ._ratio .getValue (), 1, 20);
   },
   set_release__ ()
   {
      this .dynamicsCompressorNode .release .value = Math_Algorithm .clamp (this ._release .getValue (), 0, 1);
   },
   set_threshold__ ()
   {
      this .dynamicsCompressorNode .threshold .value = Math_Algorithm .clamp (this ._threshold .getValue (), -100, 0);
   },
   set_time ()
   {
      this ._reduction = this .dynamicsCompressorNode .reduction;

      Sound_X3DSoundProcessingNode .prototype .set_time .call (this);
   },
});

Object .defineProperties (DynamicsCompressor,
{
   ... Core_X3DNode .getStaticProperties ("DynamicsCompressor", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "attack",                new x_ite_Fields .SFTime (0.003)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "knee",                  new x_ite_Fields .SFFloat (30)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "ratio",                 new x_ite_Fields .SFFloat (12)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "release",               new x_ite_Fields .SFTime (0.25)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "threshold",             new x_ite_Fields .SFFloat (-24)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "reduction",             new x_ite_Fields .SFFloat ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const DynamicsCompressor_default_ = DynamicsCompressor;
;

/* harmony default export */ const Sound_DynamicsCompressor = (Namespace/* default */.A .add ("DynamicsCompressor", DynamicsCompressor_default_));
;// ./src/x_ite/Components/Sound/Gain.js







function Gain (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Gain);
}

Object .setPrototypeOf (Gain .prototype, Sound_X3DSoundProcessingNode .prototype);

Object .defineProperties (Gain,
{
   ... Core_X3DNode .getStaticProperties ("Gain", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Gain_default_ = Gain;
;

/* harmony default export */ const Sound_Gain = (Namespace/* default */.A .add ("Gain", Gain_default_));
;// ./src/x_ite/Components/Sound/ListenerPointSource.js










function ListenerPointSource (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .ListenerPointSource);

   this .setVisibleObject (true);

   // Units

   this ._position           .setUnit ("length");
   this ._interauralDistance .setUnit ("length");
}

Object .assign (Object .setPrototypeOf (ListenerPointSource .prototype, Sound_X3DSoundNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundNode .prototype .initialize .call (this);

      this ._enabled          .addInterest ("set_trackCurrentView__", this)
      this ._trackCurrentView .addInterest ("set_trackCurrentView__", this);

      this .set_trackCurrentView__ ();
   },
   set_trackCurrentView__ ()
   {
      if (!this ._enabled .getValue ())
         return;

      if (!this ._trackCurrentView .getValue ())
         return;

      const
         audioContext = this .getBrowser () .getAudioContext (),
         listener     = audioContext .listener;

      listener .positionX .value = 0;
      listener .positionY .value = 0;
      listener .positionZ .value = 0;

      listener .forwardX .value = 0;
      listener .forwardY .value = 0;
      listener .forwardZ .value = -1;

      listener .upX .value = 0;
      listener .upY .value = 1;
      listener .upZ .value = 0;
   },
   traverse: (() =>
   {
      const
         position        = new Numbers_Vector3 (),
         orientation     = new Numbers_Rotation4 (),
         modelViewMatrix = new Numbers_Matrix4 (),
         forwardVector   = new Numbers_Vector3 (),
         upVector        = new Numbers_Vector3 ();

      return function (type, renderObject)
      {
         if (!this ._enabled .getValue ())
            return;

         if (this ._trackCurrentView .getValue ())
            return;

         const
            audioContext = this .getBrowser () .getAudioContext (),
            listener     = audioContext .listener;

         modelViewMatrix
            .assign (renderObject .getModelViewMatrix () .get ())
            .translate (this ._position .getValue ())
            .rotate (this ._orientation .getValue ())
            .getTransform (position, orientation);

         orientation .multVecRot (forwardVector .assign (Numbers_Vector3 .Z_AXIS) .negate ()) .normalize ();
         orientation .multVecRot (upVector .assign (Numbers_Vector3 .Y_AXIS)) .normalize ();

         listener .positionX .value = position .x;
         listener .positionY .value = position .y;
         listener .positionZ .value = position .z;

         listener .forwardX .value = forwardVector .x;
         listener .forwardY .value = forwardVector .y;
         listener .forwardZ .value = forwardVector .z;

         listener .upX .value = upVector .x;
         listener .upY .value = upVector .y;
         listener .upZ .value = upVector .z;
      };
   })(),
});

Object .defineProperties (ListenerPointSource,
{
   ... Core_X3DNode .getStaticProperties ("ListenerPointSource", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",              new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "trackCurrentView",     new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "position",             new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "orientation",          new x_ite_Fields .SFRotation ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "interauralDistance",   new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "dopplerEnabled",       new x_ite_Fields .SFBool ()),
      ]),
      enumerable: true,
   },
});

const ListenerPointSource_default_ = ListenerPointSource;
;

/* harmony default export */ const Sound_ListenerPointSource = (Namespace/* default */.A .add ("ListenerPointSource", ListenerPointSource_default_));
;// ./src/x_ite/Components/Sound/MicrophoneSource.js







function MicrophoneSource (executionContext)
{
   Sound_X3DSoundSourceNode .call (this, executionContext);

   this .addType (Base_X3DConstants .MicrophoneSource);

   this .addChildObjects (Base_X3DConstants .inputOutput, "loop", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (MicrophoneSource .prototype, Sound_X3DSoundSourceNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);

      this ._mediaDeviceID .addInterest ("set_mediaDeviceID__", this);
   },
   set_mediaDeviceID__ ()
   {
      this .set_stop ();

      if (this ._isActive .getValue ())
         this .set_start ();
   },
   set_start ()
   {
      if (!navigator .mediaDevices)
         return;

      this .restore = false;

      navigator .mediaDevices .getUserMedia ({
         audio:
         {
            deviceId: this ._mediaDeviceID .getValue (),
         },
      })
      .then (mediaStream =>
      {
         const audioContext = this .getBrowser () .getAudioContext ();

         this .mediaStreamAudioSourceNode = new MediaStreamAudioSourceNode (audioContext, { mediaStream });

         if (this ._isActive .getValue ())
         {
            if (this ._isPaused .getValue () || !this .getLive () .getValue ())
               this .set_pause ();
            else
               this .set_resume ();
         }
         else
         {
            this .set_stop ();
         }
      })
      .catch (error =>
      {
         console .error (error .message);
      });
   },
   set_pause ()
   {
      if (!this .mediaStreamAudioSourceNode)
         return;

      if (this .getLive () .getValue ())
      {
         this .mediaStreamAudioSourceNode .disconnect ();

         for (const track of this .mediaStreamAudioSourceNode .mediaStream .getAudioTracks ())
            track .enabled = false;

         for (const track of this .mediaStreamAudioSourceNode .mediaStream .getVideoTracks ())
            track .enabled = false;
      }
      else
      {
         this .set_stop (true);
      }
   },
   set_resume ()
   {
      if (this .restore)
         return this .set_start ();

      if (!this .mediaStreamAudioSourceNode)
         return;

      this .mediaStreamAudioSourceNode .connect (this .getAudioSource ());

      for (const track of this .mediaStreamAudioSourceNode .mediaStream .getAudioTracks ())
         track .enabled = true;

      for (const track of this .mediaStreamAudioSourceNode .mediaStream .getVideoTracks ())
         track .enabled = true;
   },
   set_stop (restore = false)
   {
      if (!this .mediaStreamAudioSourceNode)
         return;

      this .mediaStreamAudioSourceNode .disconnect ();

      for (const track of this .mediaStreamAudioSourceNode .mediaStream .getAudioTracks ())
         track .stop ();

      for (const track of this .mediaStreamAudioSourceNode .mediaStream .getVideoTracks ())
         track .stop ();

      this .mediaStreamAudioSourceNode = null;
      this .restore                    = restore;
   },
});

Object .defineProperties (MicrophoneSource,
{
   ... Core_X3DNode .getStaticProperties ("MicrophoneSource", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",   new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",       new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",          new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mediaDeviceID", new x_ite_Fields .SFString ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",     new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",    new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",     new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",      new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",      new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",   new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const MicrophoneSource_default_ = MicrophoneSource;
;

/* harmony default export */ const Sound_MicrophoneSource = (Namespace/* default */.A .add ("MicrophoneSource", MicrophoneSource_default_));
;// ./src/x_ite/Components/Sound/OscillatorSource.js








function OscillatorSource (executionContext)
{
   Sound_X3DSoundSourceNode .call (this, executionContext);

   this .addType (Base_X3DConstants .OscillatorSource);

   this .addChildObjects (Base_X3DConstants .inputOutput, "loop", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (OscillatorSource .prototype, Sound_X3DSoundSourceNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);

      this ._detune       .addInterest ("set_detune__",       this);
      this ._frequency    .addInterest ("set_frequency__",    this);
      this ._periodicWave .addInterest ("set_periodicWave__", this);

      this .set_periodicWave__ ();
   },
   set_detune__ ()
   {
      if (!this .oscillatorNode)
         return;

      this .oscillatorNode .detune .value = Math .max (this ._detune .getValue (), 0);
   },
   set_frequency__ ()
   {
      if (!this .oscillatorNode)
         return;

      this .oscillatorNode .frequency .value = Math .max (this ._frequency .getValue (), 0);
   },
   set_periodicWave__ ()
   {
      if (this .periodicWaveNode)
      {
         this .periodicWaveNode ._type        .removeInterest ("set_type__",                this);
         this .periodicWaveNode ._optionsReal .removeInterest ("set_periodicWaveOptions__", this);
         this .periodicWaveNode ._optionsImag .removeInterest ("set_periodicWaveOptions__", this);
      }

      this .periodicWaveNode = Base_X3DCast (Base_X3DConstants .PeriodicWave, this ._periodicWave)
         ?? this .getBrowser () .getDefaultPeriodicWave ();

      this .periodicWaveNode ._type .addInterest ("set_type__", this);

      this .set_type__ ();
   },
   set_type__: (() =>
   {
      const types = new Map ([
         ["SINE",     "sine"],
         ["SQUARE",   "square"],
         ["SAWTOOTH", "sawtooth"],
         ["TRIANGLE", "triangle"],
         ["CUSTOM",   "custom"],
      ]);

      return function ()
      {
         if (!this .oscillatorNode)
            return;

         this .periodicWaveNode ._optionsReal .removeInterest ("set_periodicWaveOptions__", this);
         this .periodicWaveNode ._optionsImag .removeInterest ("set_periodicWaveOptions__", this);

         const type = types .get (this .periodicWaveNode ._type .getValue ()) ?? "square";

         if (type === "custom")
         {
            this .periodicWaveNode ._optionsReal .addInterest ("set_periodicWaveOptions__", this);
            this .periodicWaveNode ._optionsImag .addInterest ("set_periodicWaveOptions__", this);

            this .set_periodicWaveOptions__ ();
         }
         else
         {
            this .oscillatorNode .type = type;
         }
      };
   })(),
   set_periodicWaveOptions__ ()
   {
      this .oscillatorNode .setPeriodicWave (this .periodicWaveNode .createPeriodicWave ());
   },
   set_start ()
   {
      const audioContext = this .getBrowser () .getAudioContext ();

      this .oscillatorNode = new OscillatorNode (audioContext);

      this .set_detune__ ();
      this .set_frequency__ ();
      this .set_type__ ();

      this .oscillatorNode .connect (this .getAudioSource ());
      this .oscillatorNode .start ();
   },
   set_pause ()
   {
      this .set_stop ();
   },
   set_resume ()
   {
      this .set_start ();
   },
   set_stop ()
   {
      this .oscillatorNode .stop ();
      this .oscillatorNode .disconnect ();
   },
});

Object .defineProperties (OscillatorSource,
{
   ... Core_X3DNode .getStaticProperties ("OscillatorSource", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",     new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",  new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",      new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",         new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "detune",       new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "frequency",    new x_ite_Fields .SFFloat (440)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "periodicWave", new x_ite_Fields .SFNode ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",    new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",   new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",    new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",     new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",     new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",     new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",  new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const OscillatorSource_default_ = OscillatorSource;
;

/* harmony default export */ const Sound_OscillatorSource = (Namespace/* default */.A .add ("OscillatorSource", OscillatorSource_default_));
;// ./src/x_ite/Components/Sound/Sound.js














function Sound (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .Sound);

   this .addChildObjects (Base_X3DConstants .outputOnly, "traversed", new x_ite_Fields .SFBool (true));

   this .setVisibleObject (true);

   // Units

   this ._location .setUnit ("length");
   this ._minBack  .setUnit ("length");
   this ._minFront .setUnit ("length");
   this ._maxBack  .setUnit ("length");
   this ._maxFront .setUnit ("length");

   // Private properties

   this .childNodes       = [ ];
   this .currentTraversed = true;
}

Object .assign (Object .setPrototypeOf (Sound .prototype, Sound_X3DSoundNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundNode .prototype .initialize .call (this);

      const
         audioContext       = this .getBrowser () .getAudioContext (),
         gainNode           = new GainNode (audioContext, { gain: 0 }),
         splitterNode       = new ChannelSplitterNode (audioContext, { numberOfOutputs: 2 }),
         mergerNode         = new ChannelMergerNode (audioContext, { numberOfInputs: 2 }),
         gainFrontLeftNode  = new GainNode (audioContext, { gain: 0 }),
         gainFrontRightNode = new GainNode (audioContext, { gain: 0 }),
         gainBackLeftNode   = new GainNode (audioContext, { gain: 0 }),
         gainBackRightNode  = new GainNode (audioContext, { gain: 0 });

      gainNode .channelCount          = 2;
      gainNode .channelCountMode      = "explicit";
      gainNode .channelInterpretation = "speakers";

      gainNode           .connect (splitterNode);
      splitterNode       .connect (gainFrontLeftNode,  0);
      splitterNode       .connect (gainFrontRightNode, 1);
      splitterNode       .connect (gainBackRightNode, 0);
      splitterNode       .connect (gainBackLeftNode,  1);
      gainFrontLeftNode  .connect (mergerNode, 0, 0);
      gainFrontRightNode .connect (mergerNode, 0, 1);
      gainBackLeftNode   .connect (mergerNode, 0, 0);
      gainBackRightNode  .connect (mergerNode, 0, 1);
      mergerNode         .connect (audioContext .destination);

      this .gainNode           = gainNode;
      this .splitterNode       = splitterNode;
      this .gainFrontLeftNode  = gainFrontLeftNode;
      this .gainFrontRightNode = gainFrontRightNode;
      this .gainBackLeftNode   = gainBackLeftNode;
      this .gainBackRightNode  = gainBackRightNode;
      this .mergerNode         = mergerNode;

      this .getLive () .addInterest ("set_live__", this);
      this ._traversed .addInterest ("set_live__", this);

      this ._intensity .addInterest ("set_intensity__", this);
      this ._source    .addInterest ("set_children__",  this);
      this ._children  .addInterest ("set_children__",  this);

      this .set_live__ ();
      this .set_intensity__ ();
      this .set_children__ ();
   },
   setTraversed (value)
   {
      if (value)
      {
         if (this ._traversed .getValue () === false)
            this ._traversed = true;
      }
      else
      {
         if (this .currentTraversed !== this ._traversed .getValue ())
            this ._traversed = this .currentTraversed;
      }

      this .currentTraversed = value;
   },
   getTraversed ()
   {
      return this .currentTraversed;
   },
   setGain (gain, pan = 0.5, rotation = 0)
   {
      const
         panLeft       = 1 - pan ** 2,
         panRight      = 1 - (1 - pan) ** 2,
         rotationFront = 1 - rotation,
         rotationBack  = rotation;

      this .gainFrontLeftNode  .gain .value = gain * rotationFront * panLeft;
      this .gainFrontRightNode .gain .value = gain * rotationFront * panRight;
      this .gainBackLeftNode   .gain .value = gain * rotationBack  * panLeft;
      this .gainBackRightNode  .gain .value = gain * rotationBack  * panRight;
   },
   set_live__ ()
   {
      this .mergerNode .disconnect ();

      if (this .getLive () .getValue () && this ._traversed .getValue ())
      {
         const audioContext = this .getBrowser () .getAudioContext ();

         this .getBrowser () .sensorEvents () .addInterest ("update", this);

         this .mergerNode .connect (audioContext .destination);
      }
      else
      {
         this .getBrowser () .sensorEvents () .removeInterest ("update", this);
      }
   },
   set_intensity__ ()
   {
      this .gainNode .gain .value = Math_Algorithm .clamp (this ._intensity .getValue (), 0, 1);
   },
   set_children__ ()
   {
      for (const childNode of this .childNodes)
         childNode .getAudioSource () .disconnect (this .gainNode);

      this .childNodes .length = 0;

      const sourceNode = Base_X3DCast (Base_X3DConstants .X3DSoundSourceNode, this ._source);

      if (sourceNode)
         this .childNodes .push (sourceNode);

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
               case Base_X3DConstants .X3DSoundSourceNode:
                  this .childNodes .push (childNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      for (const childNode of this .childNodes)
         childNode .getAudioSource () .connect (this .gainNode);

      this .setVisibleObject (this .childNodes .length);
   },
   update ()
   {
      this .setTraversed (false);
   },
   traverse: (() =>
   {
      const
         min = { distance: 0, intersection: new Numbers_Vector3 () },
         max = { distance: 0, intersection: new Numbers_Vector3 () };

      return function (type, renderObject)
      {
         this .setTraversed (true);

         const modelViewMatrix = renderObject .getModelViewMatrix () .get ();

         this .getEllipsoidParameter (modelViewMatrix,
                                      Math .max (this ._maxBack  .getValue (), 0),
                                      Math .max (this ._maxFront .getValue (), 0),
                                      max);

         if (max .distance < 1) // Radius of normalized sphere is 1.
         {
            this .getEllipsoidParameter (modelViewMatrix,
                                         Math .max (this ._minBack  .getValue (), 0),
                                         Math .max (this ._minFront .getValue (), 0),
                                         min);

            const { pan, rotation } = this .getPan (modelViewMatrix);

            if (min .distance < 1) // Radius of normalized sphere is 1.
            {
               this .setGain (1, pan, rotation);
            }
            else
            {
               const
                  d1 = max .intersection .norm (), // Viewer is here at (0, 0, 0)
                  d2 = max .intersection .distance (min .intersection),
                  d  = Math .min (d1 / d2, 1);

               this .setGain (d, pan, rotation);
            }
         }
         else
         {
            this .setGain (0);
         }
      };
   })(),
   getEllipsoidParameter: (() =>
   {
      const
         location        = new Numbers_Vector3 (),
         sphereMatrix    = new Numbers_Matrix4 (),
         invSphereMatrix = new Numbers_Matrix4 (),
         rotation        = new Numbers_Rotation4 (),
         scale           = new Numbers_Vector3 (),
         sphere          = new Geometry_Sphere3 (1, Numbers_Vector3 .ZERO),
         normal          = new Numbers_Vector3 (),
         line            = new Geometry_Line3 (),
         enterPoint      = new Numbers_Vector3 (),
         exitPoint       = new Numbers_Vector3 ();

      return function (modelViewMatrix, back, front, value)
      {
         /*
          * https://de.wikipedia.org/wiki/Ellipse
          *
          * The ellipsoid is transformed to a sphere for easier calculation and then the viewer position is
          * transformed into this coordinate system. The radius and distance can then be obtained.
          *
          * throws Error
          */

         if (back == 0 || front == 0)
         {
            sphereMatrix .multVecMatrix (value .intersection .assign (this ._location .getValue ()));
            value .distance = 1;
            return;
         }

         const
            a = (back + front) / 2,
            e = a - back,
            b = Math .sqrt (a * a - e * e);

         location .set (0, 0, e);
         scale    .set (b, b, a);
         rotation .setVectors (Numbers_Vector3 .Z_AXIS, this ._direction .getValue ());

         sphereMatrix
            .assign (modelViewMatrix)
            .translate (this ._location .getValue ())
            .rotate (rotation)
            .translate (location)
            .scale (scale);

         invSphereMatrix .assign (sphereMatrix) .inverse ();

         const viewer = invSphereMatrix .origin;
         location .negate () .divVec (scale);

         normal .assign (location) .subtract (viewer) .normalize ();
         line .set (viewer, normal);
         sphere .intersectsLine (line, enterPoint, exitPoint);

         value .intersection .assign (sphereMatrix .multVecMatrix (enterPoint));
         value .distance = viewer .norm ();
      };
   })(),
   getPan: (() =>
   {
      const
         rotation  = new Numbers_Rotation4 (),
         location  = new Numbers_Vector3 (),
         direction = new Numbers_Vector3 (),
         xAxis     = new Numbers_Vector3 (),
         result    = { };

      return function (modelViewMatrix)
      {
         if (!this ._spatialize .getValue ())
         {
            result .pan      = 0.5;
            result .rotation = 0;
            return result;
         }

         location  .assign (this ._location  .getValue ());
         direction .assign (this ._direction .getValue ());
         rotation .setVectors (Numbers_Vector3 .Z_AXIS, direction) .straighten ();
         rotation .multVecRot (xAxis .assign (Numbers_Vector3 .X_AXIS));

         modelViewMatrix .multVecMatrix (location) .normalize ();
         modelViewMatrix .multDirMatrix (xAxis)    .normalize ();

         result .pan      = 1 - Math .acos (Math_Algorithm .clamp (location .dot (Numbers_Vector3 .X_AXIS), -1, 1)) / Math .PI;
         result .rotation =     Math .acos (Math_Algorithm .clamp (xAxis    .dot (Numbers_Vector3 .X_AXIS), -1, 1)) / Math .PI;

         return result;
      };
   })(),
});

Object .defineProperties (Sound,
{
   ... Core_X3DNode .getStaticProperties ("Sound", "Sound", 1, "children", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enabled",     new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "spatialize",  new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",   new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "location",    new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "direction",   new x_ite_Fields .SFVec3f (0, 0, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "minBack",     new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "minFront",    new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "maxBack",     new x_ite_Fields .SFFloat (10)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "maxFront",    new x_ite_Fields .SFFloat (10)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "priority",    new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "source",      new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",    new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const Sound_default_ = Sound;
;

/* harmony default export */ const Sound_Sound = (Namespace/* default */.A .add ("Sound", Sound_default_));
;// ./src/x_ite/Components/Sound/SpatialSound.js










function SpatialSound (executionContext)
{
   Sound_X3DSoundNode .call (this, executionContext);

   this .addType (Base_X3DConstants .SpatialSound);

   this .addChildObjects (Base_X3DConstants .outputOnly, "traversed", new x_ite_Fields .SFBool (true));

   this .setVisibleObject (true);

   // Units

   this ._location          .setUnit ("length");
   this ._coneInnerAngle    .setUnit ("angle");
   this ._coneOuterAngle    .setUnit ("angle");
   this ._maxDistance       .setUnit ("length");
   this ._referenceDistance .setUnit ("length");

   // Private properties

   this .childNodes       = [ ];
   this .currentTraversed = true;
}

Object .assign (Object .setPrototypeOf (SpatialSound .prototype, Sound_X3DSoundNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundNode .prototype .initialize .call (this);

      const
         audioContext = this .getBrowser () .getAudioContext (),
         gainNode     = new GainNode (audioContext, { gain: 0 }),
         pannerNode   = new PannerNode (audioContext);

      gainNode .channelCount          = 2;
      gainNode .channelCountMode      = "explicit";
      gainNode .channelInterpretation = "speakers";

      gainNode   .connect (pannerNode);
      pannerNode .connect (audioContext .destination);

      this .gainNode   = gainNode;
      this .pannerNode = pannerNode;

      this .getLive () .addInterest ("set_live__", this);
      this ._traversed .addInterest ("set_live__", this);

      this ._spatialize        .addInterest ("set_spatialize__",        this);
      this ._intensity         .addInterest ("set_intensity__",         this);
      this ._gain              .addInterest ("set_intensity__",         this);
      this ._enableHRTF        .addInterest ("set_enableHRTF__",        this);
      this ._dopplerEnabled    .addInterest ("set_dopplerEnabled__",    this);
      this ._coneOuterGain     .addInterest ("set_coneOuterGain__",     this);
      this ._coneInnerAngle    .addInterest ("set_coneInnerAngle__",    this);
      this ._coneOuterAngle    .addInterest ("set_coneOuterAngle__",    this);
      this ._distanceModel     .addInterest ("set_distanceModel__",     this);
      this ._maxDistance       .addInterest ("set_maxDistance__",       this);
      this ._referenceDistance .addInterest ("set_referenceDistance__", this);
      this ._rolloffFactor     .addInterest ("set_distanceModel__",     this);
      this ._children          .addInterest ("set_children__",          this);

      this .set_live__ ();
      this .set_spatialize__ ();
      this .set_intensity__ ();
      this .set_coneOuterGain__ ();
      this .set_coneInnerAngle__ ();
      this .set_coneOuterAngle__ ();
      this .set_distanceModel__ ();
      this .set_maxDistance__ ();
      this .set_referenceDistance__ ();
      this .set_enableHRTF__ ();
      this .set_dopplerEnabled__ ();
      this .set_children__ ();
   },
   setTraversed (value)
   {
      if (value)
      {
         if (this ._traversed .getValue () === false)
            this ._traversed = true;
      }
      else
      {
         if (this .currentTraversed !== this ._traversed .getValue ())
            this ._traversed = this .currentTraversed;
      }

      this .currentTraversed = value;
   },
   getTraversed ()
   {
      return this .currentTraversed;
   },
   set_live__ ()
   {
      this .pannerNode .disconnect ();

      if (this .getLive () .getValue () && this ._traversed .getValue ())
      {
         const audioContext = this .getBrowser () .getAudioContext ();

         this .getBrowser () .sensorEvents () .addInterest ("update", this);

         this .pannerNode .connect (audioContext .destination);
      }
      else
      {
         this .getBrowser () .sensorEvents () .removeInterest ("update", this);
      }
   },
   set_spatialize__ ()
   {
      const audioContext = this .getBrowser () .getAudioContext ();

      this .gainNode .disconnect ();

      if (this ._spatialize .getValue ())
         this .gainNode .connect (this .pannerNode);
      else
         this .gainNode .connect (audioContext .destination);
   },
   set_intensity__ ()
   {
      this .gainNode .gain .value = Math_Algorithm .clamp (this ._intensity .getValue (), 0, 1) * this ._gain .getValue ();
   },
   set_coneOuterGain__ ()
   {
      this .pannerNode .coneOuterGain = Math_Algorithm .clamp (this ._coneOuterGain .getValue (), 0, 1);
   },
   set_coneInnerAngle__ ()
   {
      this .pannerNode .coneInnerAngle = Math_Algorithm .clamp (Math_Algorithm .degrees (this ._coneInnerAngle .getValue ()), 0, 360);
   },
   set_coneOuterAngle__ ()
   {
      this .pannerNode .coneOuterAngle = Math_Algorithm .clamp (Math_Algorithm .degrees (this ._coneOuterAngle .getValue ()), 0, 360);
   },
   set_distanceModel__: (() =>
   {
      const distanceModels = new Map ([
         ["LINEAR",      "linear"],
         ["INVERSE",     "inverse"],
         ["EXPONENTIAL", "exponential"],
      ]);

      return function ()
      {
         this .pannerNode .distanceModel = distanceModels .get (this ._distanceModel .getValue ()) ?? "inverse";

         let rolloffFactor = Math .max (this ._rolloffFactor .getValue (), 0);

         if (this .pannerNode .distanceModel === "linear")
            rolloffFactor = Math .min (rolloffFactor, 1);

         this .pannerNode .rolloffFactor = rolloffFactor;
      };
   })(),
   set_maxDistance__ ()
   {
      this .pannerNode .maxDistance = Math .max (this ._maxDistance .getValue (), 0);
   },
   set_referenceDistance__ ()
   {
      this .pannerNode .refDistance = Math .max (this ._referenceDistance .getValue (), 0);
   },
   set_enableHRTF__ ()
   {
      if (this ._enableHRTF .getValue ())
         this .pannerNode .panningModel = "HRTF";
      else
         this .pannerNode .panningModel = "equalpower";
   },
   set_dopplerEnabled__ ()
   {
      // Depreciated: https://github.com/WebAudio/web-audio-api/issues/372.
   },
   set_children__ ()
   {
      for (const childNode of this .childNodes)
         childNode .getAudioSource () .disconnect (this .gainNode);

      this .childNodes .length = 0;

      for (const child of this ._children)
      {
         const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

         if (!childNode)
            continue;

         const type = childNode .getType ();

         for (let t = type .length - 1; t >= 0; -- t)
         {
            switch (type [t])
            {
               case Base_X3DConstants .X3DSoundChannelNode:
               case Base_X3DConstants .X3DSoundProcessingNode:
               case Base_X3DConstants .X3DSoundSourceNode:
                  this .childNodes .push (childNode);
                  break;
               default:
                  continue;
            }

            break;
         }
      }

      for (const childNode of this .childNodes)
         childNode .getAudioSource () .connect (this .gainNode);
   },
   update ()
   {
      this .setTraversed (false);
   },
   traverse: (() =>
   {
      const
         location  = new Numbers_Vector3 (),
         direction = new Numbers_Vector3 ();

      return function (type, renderObject)
      {
         this .setTraversed (true);

         const modelViewMatrix = renderObject .getModelViewMatrix () .get ();

         modelViewMatrix .multVecMatrix (location  .assign (this ._location  .getValue ()));
         modelViewMatrix .multDirMatrix (direction .assign (this ._direction .getValue ())) .normalize ();

         this .pannerNode .positionX .value = location .x;
         this .pannerNode .positionY .value = location .y;
         this .pannerNode .positionZ .value = location .z;

         this .pannerNode .orientationX .value = direction .x;
         this .pannerNode .orientationY .value = direction .y;
         this .pannerNode .orientationZ .value = direction .z;
      };
   })(),
});

Object .defineProperties (SpatialSound,
{
   ... Core_X3DNode .getStaticProperties ("SpatialSound", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enabled",           new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "spatialize",        new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "intensity",         new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "location",          new x_ite_Fields .SFVec3f ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "direction",         new x_ite_Fields .SFVec3f (0, 0, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "gain",              new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coneOuterGain",     new x_ite_Fields .SFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coneInnerAngle",    new x_ite_Fields .SFFloat (6.2832)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "coneOuterAngle",    new x_ite_Fields .SFFloat (6.2832)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "distanceModel",     new x_ite_Fields .SFString ("INVERSE")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "maxDistance",       new x_ite_Fields .SFFloat (10000)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "referenceDistance", new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "rolloffFactor",     new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enableHRTF",        new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "dopplerEnabled",    new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "priority",          new x_ite_Fields .SFFloat ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",          new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const SpatialSound_default_ = SpatialSound;
;

/* harmony default export */ const Sound_SpatialSound = (Namespace/* default */.A .add ("SpatialSound", SpatialSound_default_));
;// ./src/x_ite/Components/Sound/StreamAudioDestination.js







/**
 * THIS NODE IS NOT SUPPORTED.
 */

function StreamAudioDestination (executionContext)
{
   Sound_X3DSoundDestinationNode .call (this, executionContext);

   this .addType (Base_X3DConstants .StreamAudioDestination);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .mediaStreamAudioDestinationNode = new MediaStreamAudioDestinationNode (audioContext);
}

Object .assign (Object .setPrototypeOf (StreamAudioDestination .prototype, Sound_X3DSoundDestinationNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundDestinationNode .prototype .initialize .call (this);

      this ._mediaDeviceID .addInterest ("set_mediaDeviceID__", this);

      this .set_mediaDeviceID__ ();
   },
   getSoundDestination ()
   {
      return this .mediaStreamAudioDestinationNode;
   },
   set_enabled__ () { /* remove this function if implemented */ },
   set_mediaDeviceID__ ()
   {

   },
});

Object .defineProperties (StreamAudioDestination,
{
   ... Core_X3DNode .getStaticProperties ("StreamAudioDestination", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mediaDeviceID",         new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "streamIdentifier",      new x_ite_Fields .MFString ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const StreamAudioDestination_default_ = StreamAudioDestination;
;

/* harmony default export */ const Sound_StreamAudioDestination = (Namespace/* default */.A .add ("StreamAudioDestination", StreamAudioDestination_default_));
;// ./src/x_ite/Components/Sound/StreamAudioSource.js







/**
 * THIS NODE IS NOT SUPPORTED.
 */

function StreamAudioSource (executionContext)
{
   Sound_X3DSoundSourceNode .call (this, executionContext);

   this .addType (Base_X3DConstants .StreamAudioSource);

   this .addChildObjects (Base_X3DConstants .inputOutput, "loop", new x_ite_Fields .SFBool ());
}

Object .assign (Object .setPrototypeOf (StreamAudioSource .prototype, Sound_X3DSoundSourceNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);
   },
});


Object .defineProperties (StreamAudioSource,
{
   ... Core_X3DNode .getStaticProperties ("StreamAudioSource", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",      new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",          new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",             new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "streamIdentifier", new x_ite_Fields .MFString ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",        new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",       new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",        new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",         new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",         new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",      new x_ite_Fields .SFTime ()),
      ]),
      enumerable: true,
   },
});

const StreamAudioSource_default_ = StreamAudioSource;
;

/* harmony default export */ const Sound_StreamAudioSource = (Namespace/* default */.A .add ("StreamAudioSource", StreamAudioSource_default_));
;// ./src/x_ite/Components/Sound/WaveShaper.js







function WaveShaper (executionContext)
{
   Sound_X3DSoundProcessingNode .call (this, executionContext);

   this .addType (Base_X3DConstants .WaveShaper);

   const audioContext = this .getBrowser () .getAudioContext ();

   this .waveShaperNode = new WaveShaperNode (audioContext);

   this .waveShaperNode .connect (this .getAudioSource ());
}

Object .assign (Object .setPrototypeOf (WaveShaper .prototype, Sound_X3DSoundProcessingNode .prototype),
{
   initialize ()
   {
      Sound_X3DSoundProcessingNode .prototype .initialize .call (this);

      this ._curve      .addInterest ("set_curve__",      this);
      this ._oversample .addInterest ("set_oversample__", this);

      this .setSoundProcessor (this .waveShaperNode);

      this .set_curve__ ();
      this .set_oversample__ ();
   },
   set_curve__ ()
   {
      this .waveShaperNode .curve = this ._curve .length < 2
         ? new Float32Array (2)
         : this ._curve .shrinkToFit ();
   },
   set_oversample__: (() =>
   {
      const oversampleTypes = new Map ([
         ["NONE", "none"],
         ["2X",   "2x"],
         ["4X",   "4x"],
      ]);

      return function ()
      {
         this .waveShaperNode .oversample = oversampleTypes .get (this ._oversample .getValue ()) ?? "none";
      };
   })(),
});

Object .defineProperties (WaveShaper,
{
   ... Core_X3DNode .getStaticProperties ("WaveShaper", "Sound", 2, "children", "4.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",              new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description",           new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "enabled",               new x_ite_Fields .SFBool (true)),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "gain",                  new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "tailTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "curve",                 new x_ite_Fields .MFFloat ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "oversample",            new x_ite_Fields .SFString ("NONE")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCount",          new x_ite_Fields .SFInt32 ()), // skip test
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelCountMode",      new x_ite_Fields .SFString ("MAX")),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "channelInterpretation", new x_ite_Fields .SFString ("SPEAKERS")),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "startTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "resumeTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "pauseTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "stopTime",              new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isPaused",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "isActive",              new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,  "elapsedTime",           new x_ite_Fields .SFTime ()),

         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "children",              new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const WaveShaper_default_ = WaveShaper;
;

/* harmony default export */ const Sound_WaveShaper = (Namespace/* default */.A .add ("WaveShaper", WaveShaper_default_));
;// ./src/x_ite/Components/SoundComponent.js



























const SoundComponent_default_ = {
   name: "Sound",
   concreteNodes:
   [
      Sound_Analyser,
      Sound_AudioClip,
      Sound_AudioDestination,
      Sound_BiquadFilter,
      Sound_BufferAudioSource,
      Sound_ChannelMerger,
      Sound_ChannelSelector,
      Sound_ChannelSplitter,
      Sound_Convolver,
      Sound_Delay,
      Sound_DynamicsCompressor,
      Sound_Gain,
      Sound_ListenerPointSource,
      Sound_MicrophoneSource,
      Sound_OscillatorSource,
      Sound_PeriodicWave,
      Sound_Sound,
      Sound_SpatialSound,
      Sound_StreamAudioDestination,
      Sound_StreamAudioSource,
      Sound_WaveShaper,
   ],
   abstractNodes:
   [
      Sound_X3DSoundChannelNode,
      Sound_X3DSoundDestinationNode,
      Sound_X3DSoundNode,
      Sound_X3DSoundProcessingNode,
      Sound_X3DSoundSourceNode,
   ],
};
;

/* harmony default export */ const SoundComponent = (Namespace/* default */.A .add ("SoundComponent", SoundComponent_default_));
;// ./src/x_ite/Browser/Texturing/GifMedia.js
function GifMedia (gif, movieTexture)
{
   let
      playbackRate = 1,
      cycle        = 0,
      loop         = false;

   Object .defineProperties (gif,
   {
      currentTime:
      {
         get ()
         {
            if (!loop && cycle < this .cycle)
               return this .duration;

            return (movieTexture ._elapsedTime * playbackRate) % this .duration;
         },
         set: Function .prototype,
      },
      duration:
      {
         get ()
         {
            return this .get_duration_ms () / 1000;
         },
      },
      loop:
      {
         get ()
         {
            return loop;
         },
         set (value)
         {
            cycle = this .cycle;
            loop  = value;
         },
      },
      cycle:
      {
         get ()
         {
            return Math .floor (movieTexture ._elapsedTime / this .duration);
         },
      },
      currentFrame:
      {
         get ()
         {
            const
               length = this .get_length (),
               frame  = Math .max (Math .ceil (this .currentTime / this .duration * length) - 1, 0);

            return this .get_frames () [frame] .data;
         },
      },
      play:
      {
         value ()
         {
            cycle = this .cycle;
            return Promise .resolve ();
         },
      },
      playbackRate:
      {
         get ()
         {
            return playbackRate;
         },
         set (value)
         {
            playbackRate = value;
         },
      },
   });

   gif .pause ();
}

const GifMedia_default_ = GifMedia;
;

/* harmony default export */ const Texturing_GifMedia = (Namespace/* default */.A .add ("GifMedia", GifMedia_default_));
;// ./src/x_ite/Browser/Texturing/PNGMedia.js
async function PNGMedia (apng, movieTexture)
{

   const
      canvas = document .createElement ("canvas"),
      cx     = canvas .getContext ("2d"),
      player = await apng .getPlayer (cx);

   canvas .width  = apng .width;
   canvas .height = apng .height;

   // Render first frame after load.
   player .stop ();

   Object .defineProperties (apng,
   {
      currentTime:
      {
         get ()
         {
            return player .ended
               ? this .duration
               : player .currentFrameNumber / apng .frames .length * this .duration;
         },
         set ()
         {
            player .stop ();
         },
      },
      duration:
      {
         get ()
         {
            return apng .playTime / 1000;
         },
      },
      loop:
      {
         get ()
         {
            return apng .numPlays === 0;
         },
         set (value)
         {
            apng .numPlays = value ? 0 : 1;
         },
      },
      cycle:
      {
         get ()
         {
            return Math .floor (movieTexture ._elapsedTime / this .duration);
         },
      },
      currentFrame:
      {
         get ()
         {
            return canvas;
         },
      },
      play:
      {
         value ()
         {
            player .play ();
            return Promise .resolve ();
         },
      },
      pause:
      {
         value ()
         {
            player .pause ();
         },
      },
      playbackRate:
      {
         get ()
         {
            return player .playbackRate;
         },
         set (value)
         {
            // No support for negative values.
            player .playbackRate = Math .max (value, 0);
         },
      },
   });
}

const PNGMedia_default_ = PNGMedia;
;

/* harmony default export */ const Texturing_PNGMedia = (Namespace/* default */.A .add ("PNGMedia", PNGMedia_default_));
;// ./src/x_ite/Components/Texturing/MovieTexture.js
/* provided dependency */ var MovieTexture_$ = __webpack_require__(44).A;
/* provided dependency */ var SuperGif = __webpack_require__(894);
/* provided dependency */ var APNG = __webpack_require__(364);













function MovieTexture (executionContext)
{
   Texturing_X3DTexture2DNode   .call (this, executionContext);
   Sound_X3DSoundSourceNode .call (this, executionContext);
   Networking_X3DUrlObject       .call (this, executionContext);

   this .addType (Base_X3DConstants .MovieTexture);

   // Private properties

   const audioContext = this .getBrowser () .getAudioContext ();

   this .urlStack               = new x_ite_Fields .MFString ();
   this .video                  = MovieTexture_$("<video></video>");
   this .sourceNode             = audioContext .createMediaElementSource (this .video [0]);
   this .mediaStreamDestination = audioContext .createMediaStreamDestination ();

   this .sourceNode .connect (this .getAudioSource ()) .connect (this .mediaStreamDestination);

   this .getMatrix () .set ([1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1]); // flipY
}

Object .assign (Object .setPrototypeOf (MovieTexture .prototype, Texturing_X3DTexture2DNode .prototype),
   Sound_X3DSoundSourceNode .prototype,
   Networking_X3DUrlObject .prototype,
{
   initialize ()
   {
      Texturing_X3DTexture2DNode   .prototype .initialize .call (this);
      Sound_X3DSoundSourceNode .prototype .initialize .call (this);
      Networking_X3DUrlObject       .prototype .initialize .call (this);

      this ._speed .addInterest ("set_speed__", this);
      this ._pitch .addInterest ("set_speed__", this);

      this .video
         .attr ("crossorigin", "anonymous")
         .attr ("preload", "auto")
         .attr ("playsinline", "");

      this .requestImmediateLoad () .catch (Function .prototype);
   },
   set_live__ ()
   {
      Sound_X3DSoundSourceNode .prototype .set_live__ .call (this);
      Networking_X3DUrlObject       .prototype .set_live__ .call (this);
   },
   getTextureType ()
   {
      return 1;
   },
   unloadData ()
   {
      this .clearTexture ();
      this .setMediaElement (null);
   },
   loadData ()
   {
      this .urlStack .assign (this ._url);
      this .loadNext ();
   },
   loadNext ()
   {
      this .clearTimeout ();

      if (this .urlStack .length === 0)
      {
         this .video .off ("abort error suspend stalled loadeddata");
         this .setMediaElement (null);
         this .clearTexture ();
         this .updateOutputs (0, 0, 0, -1);
         this .setLoadState (Base_X3DConstants .FAILED_STATE);
         return;
      }

      // Get URL.

      this .fileURL = new URL (this .urlStack .shift (), this .getExecutionContext () .getBaseURL ());

      if (this .fileURL .pathname .endsWith (".gif") || this .fileURL .href .match (/^\s*data:image\/gif[;,]/s))
      {
         new InputOutput_FileLoader (this, { dataAsString: false }) .loadDocument ([this .fileURL], async (data, fileURL) =>
         {
            if (data === null)
            {
               this .loadNext ();
            }
            else if (data instanceof ArrayBuffer)
            {
               this .fileURL = new URL (fileURL);

               const
                  img = MovieTexture_$("<img></img>") .appendTo (MovieTexture_$("<div></div>")),
                  gif = new SuperGif ({ gif: img [0], on_error: type => this .setError ({ type }) });

               gif .load_raw (new Uint8Array (data), () => this .setGif (gif));
            }
            else
            {
               throw new Error ("MovieTexture: no suitable file type handler found.");
            }
         });
      }
      else if (this .fileURL .pathname .endsWith (".png") || this .fileURL .href .match (/^\s*data:image\/png[;,]/s))
      {
         new InputOutput_FileLoader (this, { dataAsString: false }) .loadDocument ([this .fileURL], async (data, fileURL) =>
         {
            if (data === null)
            {
               this .loadNext ();
            }
            else if (data instanceof ArrayBuffer)
            {
               this .fileURL = new URL (fileURL);

               const
                  parseAPNG = DEVELOPMENT ? window ["apng-js"] .default : APNG .default,
                  apng      = await parseAPNG (data);

               this .setAPNG (apng);
            }
            else
            {
               throw new Error ("MovieTexture: no suitable file type handler found.");
            }
         });
      }
      else
      {
         if (this .fileURL .protocol !== "data:")
         {
            if (!this .getCache ())
               this .fileURL .searchParams .set ("_", Date .now ());
         }

         this .video
            .on ("abort error", this .setError .bind (this))
            .on ("suspend stalled", this .setTimeout .bind (this))
            .on ("loadeddata", this .setVideo .bind (this))
            .attr ("src", this .fileURL)
            .get (0) .load ();
      }
   },
   setTimeout (event)
   {
      this .clearTimeout ();

      this .timeoutId = setTimeout (() => this .setError (event), 30_000);
   },
   clearTimeout ()
   {
      clearTimeout (this .timeoutId);
   },
   setError (event)
   {
      if (this .fileURL .protocol !== "data:")
         console .warn (`Error loading movie '${decodeURI (this .fileURL)}':`, event .type);

      this .loadNext ();
   },
   setVideo ()
   {
      try
      {
         if (DEVELOPMENT)
         {
            if (this .fileURL .protocol !== "data:")
               console .info (`Done loading movie '${decodeURI (this .fileURL)}'.`);
         }

         const
            video  = this .video [0],
            width  = video .videoWidth,
            height = video .videoHeight;

         this .video .off ("abort error suspend stalled loadeddata");

         this .clearTimeout ();

         video .currentFrame = video;

         this .setMediaElement (video);
         this .setTextureData (width, height, true, false, video);
         this .updateOutputs (width, height, 3, video .duration);
         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);

         this .set_speed__ ();
      }
      catch (error)
      {
         // Catch security error from cross origin requests.
         this .setError ({ type: error .message });
      }
   },
   setGif (gif)
   {
      try
      {
         Texturing_GifMedia (gif, this);

         const { width, height } = gif .get_canvas ();

         this .setMediaElement (gif);
         this .setTextureData (width, height, true, false, gif .get_frames () [0] .data);
         this .updateOutputs (width, height, 4, gif .duration);
         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);

         this .set_speed__ ();
      }
      catch (error)
      {
         // Catch security error from cross origin requests.
         this .setError ({ type: error .message });
      }
   },
   async setAPNG (apng)
   {
      try
      {
         await Texturing_PNGMedia (apng, this);

         const { width, height, duration, currentFrame } = apng;

         this .setMediaElement (apng);
         this .setTextureData (width, height, true, false, currentFrame);
         this .updateOutputs (width, height, 4, duration);
         this .setLoadState (Base_X3DConstants .COMPLETE_STATE);

         this .set_speed__ ();
      }
      catch (error)
      {
         // Catch security error from cross origin requests.
         this .setError ({ type: error .message });
      }
   },
   updateOutputs (width, height, colorDepth, duration)
   {
      this ._width            = width;
      this ._height           = height;
      this ._colorDepth       = colorDepth;
      this ._duration_changed = duration;
   },
   set_gain__ ()
   {
      Sound_X3DSoundSourceNode .prototype .set_gain__ .call (this);

      this .video .prop ("muted", this ._gain .getValue () === 0);
   },
   set_speed__ ()
   {
      const media = this .getMediaElement ();

      if (!media)
         return;

      try
      {
         // Chrome throws an error if playbackRate is negative.
         media .playbackRate = this ._speed .getValue () * Math .max (this ._pitch .getValue (), 0);
      }
      catch (error)
      {
         console .error (error .message);

         media .playbackRate = 1;
      }

      media .preservesPitch = this ._pitch .getValue () === 1;
   },
   set_time ()
   {
      Sound_X3DSoundSourceNode .prototype .set_time .call (this);

      const media = this .getMediaElement ();

      if (media)
         this .updateTextureData (media .currentFrame);
   },
   dispose ()
   {
      Networking_X3DUrlObject       .prototype .dispose .call (this);
      Sound_X3DSoundSourceNode .prototype .dispose .call (this);
      Texturing_X3DTexture2DNode   .prototype .dispose .call (this);
   },
});

Object .defineProperties (MovieTexture,
{
   ... Core_X3DNode .getStaticProperties ("MovieTexture", "Texturing", 3, "texture", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",             new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",          new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enabled",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "load",                 new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",                  new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefresh",          new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "autoRefreshTimeLimit", new x_ite_Fields .SFTime (3600)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "gain",                 new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "pitch",                new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "speed",                new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "loop",                 new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "startTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "resumeTime",           new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "pauseTime",            new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "stopTime",             new x_ite_Fields .SFTime (0)),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isPaused",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "elapsedTime",          new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "width",                new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "height",               new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "colorDepth",           new x_ite_Fields .SFInt32 ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "hasSound",             new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "duration_changed",     new x_ite_Fields .SFTime ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatS",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatT",              new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "textureProperties",    new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const MovieTexture_default_ = MovieTexture;
;

/* harmony default export */ const Texturing_MovieTexture = (Namespace/* default */.A .add ("MovieTexture", MovieTexture_default_));
;// ./src/x_ite/Components/Texturing/MultiTexture.js











function MultiTexture (executionContext)
{
   Texturing_X3DTextureNode .call (this, executionContext);

   this .addType (Base_X3DConstants .MultiTexture);

   this .addChildObjects (Base_X3DConstants .outputOnly, "renderedTextures", new x_ite_Fields .SFTime ());

   // Private properties

   const browser = this .getBrowser ();

   this .maxTextures  = browser .getMaxTextures ()
   this .color        = new Float32Array (4);
   this .modes        = [ ];
   this .alphaModes   = [ ];
   this .sources      = [ ];
   this .functions    = [ ];
   this .textureNodes = [ ];
}

Object .assign (Object .setPrototypeOf (MultiTexture .prototype, Texturing_X3DTextureNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTextureNode .prototype .initialize .call (this);

      this ._color    .addInterest ("set_color__",    this);
      this ._alpha    .addInterest ("set_alpha__",    this);
      this ._mode     .addInterest ("set_mode__",     this);
      this ._source   .addInterest ("set_source__",   this);
      this ._function .addInterest ("set_function__", this);
      this ._texture  .addInterest ("set_texture__",  this);

      this .set_color__ ();
      this .set_alpha__ ();
      this .set_mode__ ();
      this .set_source__ ();
      this .set_function__ ();
      this .set_texture__ ();
   },
   getCount ()
   {
      return Math .min (this .maxTextures, this .textureNodes .length);
   },
   getMode (index)
   {
      if (index < this .modes .length)
         return this .modes [index];

      return Texturing_ModeType .MODULATE;
   },
   getAlphaMode (index)
   {
      if (index < this .alphaModes .length)
         return this .alphaModes [index];

      return Texturing_ModeType .MODULATE;
   },
   getSource (index)
   {
      if (index < this .sources .length)
         return this .sources [index];

      return Texturing_SourceType .DEFAULT;
   },
   getFunction (index)
   {
      if (index < this .functions .length)
         return this .functions [index];

      return Texturing_FunctionType .DEFAULT;
   },
   set_color__ ()
   {
      this .color [0] = this ._color .r;
      this .color [1] = this ._color .g;
      this .color [2] = this ._color .b;
   },
   set_alpha__ ()
   {
      this .color [3] = this ._alpha;
   },
   set_mode__: (() =>
   {
      const modeTypes = new Map ([
         ["REPLACE",                   Texturing_ModeType .REPLACE],
         ["MODULATE",                  Texturing_ModeType .MODULATE],
         ["MODULATE2X",                Texturing_ModeType .MODULATE2X],
         ["MODULATE4X",                Texturing_ModeType .MODULATE4X],
         ["ADD",                       Texturing_ModeType .ADD],
         ["ADDSIGNED",                 Texturing_ModeType .ADDSIGNED],
         ["ADDSIGNED2X",               Texturing_ModeType .ADDSIGNED2X],
         ["ADDSMOOTH",                 Texturing_ModeType .ADDSMOOTH],
         ["SUBTRACT",                  Texturing_ModeType .SUBTRACT],
         ["BLENDDIFFUSEALPHA",         Texturing_ModeType .BLENDDIFFUSEALPHA],
         ["BLENDTEXTUREALPHA",         Texturing_ModeType .BLENDTEXTUREALPHA],
         ["BLENDFACTORALPHA",          Texturing_ModeType .BLENDFACTORALPHA],
         ["BLENDCURRENTALPHA",         Texturing_ModeType .BLENDCURRENTALPHA],
         ["MODULATEALPHA_ADDCOLOR",    Texturing_ModeType .MODULATEALPHA_ADDCOLOR],
         ["MODULATEINVALPHA_ADDCOLOR", Texturing_ModeType .MODULATEINVALPHA_ADDCOLOR],
         ["MODULATEINVCOLOR_ADDALPHA", Texturing_ModeType .MODULATEINVCOLOR_ADDALPHA],
         ["DOTPRODUCT3",               Texturing_ModeType .DOTPRODUCT3],
         ["SELECTARG1",                Texturing_ModeType .SELECTARG1],
         ["SELECTARG2",                Texturing_ModeType .SELECTARG2],
         ["OFF",                       Texturing_ModeType .OFF],
      ]);

      return function ()
      {
         this .modes      .length = 0;
         this .alphaModes .length = 0;

         for (const modes of this ._mode)
         {
            const mode = modes .split (",");

            for (let m = 0, l = mode .length; m < l; ++ m)
               mode [m] = mode [m] .trim ();

            if (mode .length === 0)
               mode .push ("MODULATE");

            if (mode .length < 2)
               mode .push (mode [0]);

            // RGB

            const modeType = modeTypes .get (mode [0]);

            if (modeType !== undefined)
               this .modes .push (modeType);
            else
               this .modes .push (Texturing_ModeType .MODULATE);

            // Alpha

            const alphaModeType = modeTypes .get (mode [1]);

            if (alphaModeType !== undefined)
               this .alphaModes .push (alphaModeType);
            else
               this .alphaModes .push (Texturing_ModeType .MODULATE);
         }
      };
   })(),
   set_source__: (() =>
   {
      const sourceTypes = new Map ([
         ["DIFFUSE",  Texturing_SourceType .DIFFUSE],
         ["SPECULAR", Texturing_SourceType .SPECULAR],
         ["FACTOR",   Texturing_SourceType .FACTOR],
      ]);

      return function ()
      {
         this .sources .length = 0;

         for (const source of this ._source)
         {
            const sourceType = sourceTypes .get (source);

            if (sourceType !== undefined)
               this .sources .push (sourceType);
            else
               this .sources .push (Texturing_SourceType .DEFAULT);
         }
      };
   })(),
   set_function__: (() =>
   {
      const functionsTypes = new Map ([
         ["COMPLEMENT",     Texturing_FunctionType .COMPLEMENT],
         ["ALPHAREPLICATE", Texturing_FunctionType .ALPHAREPLICATE],
      ]);

      return function ()
      {
         this .functions .length = 0;

         for (const func of this ._function)
         {
            const functionsType = functionsTypes .get (func);

            if (functionsType !== undefined)
               this .functions .push (functionsType);
            else
               this .functions .push (Texturing_FunctionType .DEFAULT);
         }
      };
   })(),
   set_texture__ ()
   {
      for (const textureNode of this .textureNodes)
         textureNode ._linear .removeInterest ("addNodeEvent", this);

      this .textureNodes .length = 0;

      for (const node of this ._texture)
      {
         const textureNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureNode, node);

         if (textureNode)
            this .textureNodes .push (textureNode);
      }

      for (const textureNode of this .textureNodes)
         textureNode ._linear .addInterest ("addNodeEvent", this);

      this ._renderedTextures = this .getBrowser () .getCurrentTime ();
   },
   updateTextureBits (textureBits)
   {
      const
         maxTextures  = this .maxTextures,
         textureNodes = this .textureNodes,
         channels     = Math .min (maxTextures, textureNodes .length);

      for (let i = 0; i < channels; ++ i)
         textureNodes [i] .updateTextureBits (textureBits, i);

      textureBits .set (maxTextures * 2, 1);
   },
   getRenderedTextures (renderedTextures)
   {
      for (const textureNode of this .textureNodes)
         textureNode .getRenderedTextures (renderedTextures);
   },
   getShaderOptions (options)
   {
      const
         textureNodes = this .textureNodes,
         channels     = Math .min (this .maxTextures, textureNodes .length);

      for (let i = 0; i < channels; ++ i)
         textureNodes [i] .getShaderOptions (options, i);
   },
   setShaderUniforms (gl, uniformStruct, shaderObject)
   {
      const
         textureNodes = this .textureNodes,
         channels     = Math .min (this .maxTextures, textureNodes .length);

      gl .uniform4fv (shaderObject .x3d_MultiTextureColor, this .color);

      for (let i = 0; i < channels; ++ i)
      {
         const uniforms = shaderObject .x3d_MultiTexture [i];

         textureNodes [i] .setShaderUniforms (gl, shaderObject .x3d_Texture [i]);

         gl .uniform1i  (uniforms .mode,      this .getMode (i));
         gl .uniform1i  (uniforms .alphaMode, this .getAlphaMode (i));
         gl .uniform1i  (uniforms .source,    this .getSource (i));
         gl .uniform1i  (uniforms .function,  this .getFunction (i));
      }
   },
});

Object .defineProperties (MultiTexture,
{
   ... Core_X3DNode .getStaticProperties ("MultiTexture", "Texturing", 2, "texture", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",    new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "description", new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "color",       new x_ite_Fields .SFColor (1, 1, 1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "alpha",       new x_ite_Fields .SFFloat (1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "mode",        new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "source",      new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "function",    new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "texture",     new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const MultiTexture_default_ = MultiTexture;
;

/* harmony default export */ const Texturing_MultiTexture = (Namespace/* default */.A .add ("MultiTexture", MultiTexture_default_));
;// ./src/x_ite/Components/Texturing/MultiTextureTransform.js








function MultiTextureTransform (executionContext)
{
   Texturing_X3DTextureTransformNode .call (this, executionContext);

   this .addType (Base_X3DConstants .MultiTextureTransform);

   // Private properties

   const browser = this .getBrowser ();

   this .maxTextureTransforms  = browser .getMaxTextureTransforms ();
   this .textureTransformNodes = [ ];
}

Object .assign (Object .setPrototypeOf (MultiTextureTransform .prototype, Texturing_X3DTextureTransformNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTextureTransformNode .prototype .initialize .call (this);

      this ._textureTransform .addInterest ("set_textureTransform__", this);

      this .set_textureTransform__ ();
   },
   set_textureTransform__ ()
   {
      const textureTransformNodes = this .textureTransformNodes;

      textureTransformNodes .length = 0;

      for (const node of this ._textureTransform)
      {
         const textureTransformNode = Base_X3DCast (Base_X3DConstants .X3DSingleTextureTransformNode, node);

         if (textureTransformNode)
            textureTransformNodes .push (textureTransformNode);
      }

      if (!textureTransformNodes .length)
         textureTransformNodes .push (this .getBrowser () .getDefaultTextureTransform ());
   },
   getCount ()
   {
      return Math .min (this .maxTextureTransforms, this .textureTransformNodes .length);
   },
   getTextureTransformMapping (textureTransformMapping)
   {
      const
         textureTransformNodes = this .textureTransformNodes,
         length                = Math .min (this .maxTextureTransforms, textureTransformNodes .length);

      for (let i = 0; i < length; ++ i)
         textureTransformNodes [i] .getTextureTransformMapping (textureTransformMapping, i);
   },
   setShaderUniforms (gl, shaderObject)
   {
      const
         textureTransformNodes = this .textureTransformNodes,
         length                = Math .min (this .maxTextureTransforms, textureTransformNodes .length);

      for (let i = 0; i < length; ++ i)
         textureTransformNodes [i] .setShaderUniforms (gl, shaderObject, i);
   },
   transformPoint (texCoord)
   {
      return this .textureTransformNodes [0] .transformPoint (texCoord);
   },
});

Object .defineProperties (MultiTextureTransform,
{
   ... Core_X3DNode .getStaticProperties ("MultiTextureTransform", "Texturing", 2, "textureTransform", "3.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "metadata",         new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput, "textureTransform", new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const MultiTextureTransform_default_ = MultiTextureTransform;
;

/* harmony default export */ const Texturing_MultiTextureTransform = (Namespace/* default */.A .add ("MultiTextureTransform", MultiTextureTransform_default_));
;// ./src/x_ite/Components/Texturing/PixelTexture.js







function PixelTexture (executionContext)
{
   Texturing_X3DTexture2DNode .call (this, executionContext);

   this .addType (Base_X3DConstants .PixelTexture);

   this .addChildObjects (Base_X3DConstants .outputOnly, "loadState", new x_ite_Fields .SFInt32 (Base_X3DConstants .NOT_STARTED_STATE));
}

Object .assign (Object .setPrototypeOf (PixelTexture .prototype, Texturing_X3DTexture2DNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTexture2DNode .prototype .initialize .call (this);

      this ._image .addInterest ("set_image__", this);

      this .set_image__ ();
   },
   getTextureType ()
   {
      return 2;
   },
   checkLoadState ()
   {
      return this ._loadState .getValue ();
   },
   convert (data, comp, array, length)
   {
      switch (comp)
      {
         case 1:
         {
            for (let i = 0, index = 0; i < length; ++ i, index += 4)
            {
               const pixel = array [i];

               data [index]     =
               data [index + 1] =
               data [index + 2] = pixel & 255;
               data [index + 3] = 255;
            }

            break;
         }
         case 2:
         {
            for (let i = 0, index = 0; i < length; ++ i, index += 4)
            {
               const pixel = array [i];

               data [index]     =
               data [index + 1] =
               data [index + 2] = (pixel >>> 8) & 255;
               data [index + 3] = pixel & 255;
            }

            break;
         }
         case 3:
         {
            for (let i = 0, index = 0; i < length; ++ i, index += 4)
            {
               const pixel = array [i];

               data [index]     = (pixel >>> 16) & 255;
               data [index + 1] = (pixel >>>  8) & 255;
               data [index + 2] = pixel & 255;
               data [index + 3] = 255;
            }

            break;
         }
         case 4:
         {
            for (let i = 0, index = 0; i < length; ++ i, index += 4)
            {
               const pixel = array [i];

               data [index]     = (pixel >>> 24);
               data [index + 1] = (pixel >>> 16) & 255;
               data [index + 2] = (pixel >>>  8) & 255;
               data [index + 3] = pixel & 255;
            }

            break;
         }
      }
   },
   set_image__ ()
   {
      try
      {
         const
            comp        = this ._image .comp,
            array       = this ._image .array,
            transparent = !(comp % 2);

         const
            width  = this ._image .width,
            height = this ._image .height;

         if (width < 0 || height < 0 || comp < 0 || comp > 4)
            throw new Error (`At least one dimension (${width} × ${height} or components ${comp}) is invalid.`);

         if (width === 0 || height === 0 || comp === 0)
         {
            this .clearTexture ();
            this ._loadState = Base_X3DConstants .COMPLETE_STATE;
            return;
         }

         const data = new Uint8Array (width * height * 4);

         this .convert (data, comp, array .getValue (), array .length);
         this .setTextureData (width, height, true, transparent && this .isImageTransparent (data), data);

         this ._loadState = Base_X3DConstants .COMPLETE_STATE;
      }
      catch (error)
      {
         console .error (error);

         this .clearTexture ();
         this ._loadState = Base_X3DConstants .FAILED_STATE;
      }
   },
});

Object .defineProperties (PixelTexture,
{
   ... Core_X3DNode .getStaticProperties ("PixelTexture", "Texturing", 1, "texture", "2.0"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "image",             new x_ite_Fields .SFImage ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatS",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatT",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "textureProperties", new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
});

const PixelTexture_default_ = PixelTexture;
;

/* harmony default export */ const Texturing_PixelTexture = (Namespace/* default */.A .add ("PixelTexture", PixelTexture_default_));
;// ./src/x_ite/Rendering/DependentRenderer.js



function DependentRenderer (executionContext, renderObject)
{
   Base_X3DBaseNode     .call (this, executionContext);
   Rendering_X3DRenderObject .call (this, executionContext);

   // Private properties

   this .renderObject = renderObject;
   this .framebuffers = [ ];

   this .setup ();
}

Object .assign (Object .setPrototypeOf (DependentRenderer .prototype, Base_X3DBaseNode .prototype),
   Rendering_X3DRenderObject .prototype,
{
   initialize ()
   {
      Base_X3DBaseNode     .prototype .initialize .call (this);
      Rendering_X3DRenderObject .prototype .initialize .call (this);
   },
   isIndependent ()
   {
      return false;
   },
   getLayer ()
   {
      return this .renderObject .getLayer ();
   },
   getBackground ()
   {
      return this .backgroundNode ?? this .renderObject .getBackground ();
   },
   setBackground (backgroundNode)
   {
      this .backgroundNode = backgroundNode;
   },
   getFog ()
   {
      return this .fogNode ?? this .renderObject .getFog ();
   },
   setFog (fogNode)
   {
      this .fogNode = fogNode;
   },
   getNavigationInfo ()
   {
      return this .renderObject .getNavigationInfo ();
   },
   getViewpoint ()
   {
      return this .renderObject .getViewpoint ();
   },
   getViewpointStack ()
   {
      return this .renderObject .getViewpointStack ();
   },
   getLightContainer (path)
   {
      return this .renderObject .getLights () .get (path);
   },
   getFramebuffers ()
   {
      return this .framebuffers;
   },
   setFramebuffer (frameBuffer)
   {
      this .framebuffers [0] = frameBuffer;
   },
});

for (const key of Object .keys (DependentRenderer .prototype))
   Object .defineProperty (DependentRenderer .prototype, key, { enumerable: false });

const DependentRenderer_default_ = DependentRenderer;
;

/* harmony default export */ const Rendering_DependentRenderer = (Namespace/* default */.A .add ("DependentRenderer", DependentRenderer_default_));
;// ./src/x_ite/Components/Texturing/RenderedTexture.js















/**
 * THIS NODE IS STILL EXPERIMENTAL.
 */

function RenderedTexture (executionContext)
{
   Texturing_X3DTexture2DNode   .call (this, executionContext);
   Networking_X3DUrlOutputObject .call (this, executionContext);

   this .addType (Base_X3DConstants .RenderedTexture);

   this .addChildObjects (Base_X3DConstants .outputOnly, "loadState", new x_ite_Fields .SFInt32 (Base_X3DConstants .COMPLETE_STATE));

   // Private properties

   this .groupNode          = new Grouping_Group (executionContext);
   this .lastUpdate         = -1;
   this .dependentRenderers = new WeakMap ();
}

Object .assign (Object .setPrototypeOf (RenderedTexture .prototype, Texturing_X3DTexture2DNode .prototype),
{
   initialize ()
   {
      Texturing_X3DTexture2DNode   .prototype .initialize .call (this);
      Networking_X3DUrlOutputObject .prototype .initialize .call (this);

      this ._singleFrame .addInterest ("set_singleFrame__", this);
      this ._width       .addInterest ("set_dimensions__",  this);
      this ._height      .addInterest ("set_dimensions__",  this);
      this ._depthMap    .addInterest ("set_depthMap__",    this);
      this ._children    .addInterest ("set_children__",    this);

      this .set_singleFrame__ ();
      this .set_dimensions__ ();
      this .set_depthMap__ ();
      this .set_children__ ();

      this .groupNode .setup ();
   },
   getTextureType ()
   {
      return 2;
   },
   isRenderedTexture ()
   {
      return true;
   },
   getRenderedTextures (renderedTextures)
   {
      renderedTextures .add (this);
   },
   checkLoadState ()
   {
      return this ._loadState .getValue ();
   },
   set_singleFrame__ ()
   {
      this .update = true;
   },
   set_dimensions__ ()
   {
      const browser = this .getBrowser ();

      // Create framebuffer.

      const
         width  = Math .max (this ._width .getValue (), 0),
         height = Math .max (this ._height .getValue (), 0);

      // const components = this ._colorDepth .getValue ();

      if (width > 0 && height > 0)
      {
         // Properties

         this .viewport    = new Numbers_Vector4 (0, 0, width, height);
         this .frameBuffer = new Rendering_TextureBuffer ({ browser, width, height });

         this .setTextureData (width, height, false, false, null);
      }
      else
      {
         this .frameBuffer = null;

         this .setWidth (0);
         this .setHeight (0);
      }
   },
   set_depthMap__ ()
   {
      this .type = this ._depthMap .getValue () ? Rendering_TraverseType .DEPTH : Rendering_TraverseType .DISPLAY;
   },
   set_children__ ()
   {
      this .backgroundNode = null;
      this .fogNode        = null;
      this .viewpointNode  = null;

      this .groupNode ._children .length = 0;

      for (const child of this ._children)
         this .setChild (child);
   },
   setChild (child)
   {
      const childNode = Base_X3DCast (Base_X3DConstants .X3DChildNode, child);

      if (!childNode)
         return;

      const type = childNode .getType ();

      for (let t = type .length - 1; t >= 0; -- t)
      {
         switch (type [t])
         {
            case Base_X3DConstants .X3DBackgroundNode:
            {
               this .backgroundNode = childNode;
               break;
            }
            case Base_X3DConstants .Fog:
            {
               this .fogNode = childNode;
               break;
            }
            case Base_X3DConstants .X3DViewpointNode:
            {
               this .viewpointNode = childNode;
               break;
            }
            case Base_X3DConstants .X3DChildNode:
            {
               this .groupNode ._children .push (childNode);
               break;
            }
            default:
               continue;
         }

         break;
      }
   },
   traverse (type, renderObject)
   {
      // TraverseType .DISPLAY

      if (!renderObject .isIndependent ())
         return;

      if (!this .frameBuffer)
         return;

      if (!this .update)
         return;

      if (Date .now () - this .lastUpdate < this ._updateInterval .getValue () * 1000)
         return;

      this .lastUpdate = Date .now ();

      renderObject .getRenderedTextures () .add (this);
   },
   renderTexture: (() =>
   {
      const viewVolume = new Geometry_ViewVolume ();

      return function (renderObject)
      {
         // Make dependent renderer.

         if (!this .dependentRenderers .has (renderObject))
         {
            const dependentRenderer = new Rendering_DependentRenderer (this .getExecutionContext (), renderObject);

            this .dependentRenderers .set (renderObject, dependentRenderer);
            dependentRenderer .setDepthClearColor (1, 1, 1, 1);
         }

         // Prepare.

         this .viewpointNode ?.update ();

         const
            browser            = this .getBrowser (),
            gl                 = browser .getContext (),
            dependentRenderer  = this .dependentRenderers .get (renderObject),
            layer              = renderObject .getLayer (),
            viewport           = this .viewport,
            navigationInfoNode = dependentRenderer .getNavigationInfo (),
            viewpointNode      = this .viewpointNode ?? dependentRenderer .getViewpoint (),
            projectionMatrix   = viewpointNode .getProjectionMatrix (dependentRenderer, viewport),
            headlight          = navigationInfoNode ._headlight .getValue (),
            headlightContainer = browser .getHeadlight (),
            width              = this .getWidth (),
            height             = this .getHeight ();

         dependentRenderer .setBackground (this .backgroundNode);
         dependentRenderer .setFog (this .fogNode);
         dependentRenderer .setFramebuffer (this .frameBuffer);

         this .setTransparent (dependentRenderer .getBackground () .isTransparent ());

         // Render layer's children.

         this .frameBuffer .bind ();

         gl .viewport (... this .viewport);
         gl .scissor (... this .viewport);
         gl .clearColor (0, 0, 0, 0);
         gl .clear (gl .COLOR_BUFFER_BIT);

         dependentRenderer .getViewVolumes ()      .push (viewVolume .set (projectionMatrix, viewport, viewport));
         dependentRenderer .getProjectionMatrix () .push (projectionMatrix);

         dependentRenderer .getCameraSpaceMatrix () .push (viewpointNode .getCameraSpaceMatrix ());
         dependentRenderer .getViewMatrix ()        .push (viewpointNode .getViewMatrix ());
         dependentRenderer .getModelViewMatrix ()   .push (viewpointNode .getViewMatrix ());

         if (headlight)
         {
            headlightContainer .modelViewMatrix .push (renderObject .getViewMatrix () .get ());
            headlightContainer .modelViewMatrix .multLeft (viewpointNode .getCameraSpaceMatrix ());

            if (this .groupNode ._children .length)
            {
               dependentRenderer .getGlobalLights ()     .push (headlightContainer);
               dependentRenderer .getGlobalLightsKeys () .push (headlightContainer .lightNode .getLightKey ());
            }
         }

         if (this .groupNode ._children .length)
            dependentRenderer .render (this .type, this .groupNode .traverse, this .groupNode);
         else
            layer .traverse (this .type, dependentRenderer);

         if (headlight)
            headlightContainer .modelViewMatrix .pop ();

         dependentRenderer .getModelViewMatrix ()   .pop ();
         dependentRenderer .getViewMatrix ()        .pop ();
         dependentRenderer .getCameraSpaceMatrix () .pop ();

         dependentRenderer .getProjectionMatrix () .pop ();
         dependentRenderer .getViewVolumes ()      .pop ();

         // Transfer image.

         gl .bindTexture (this .getTarget (), this .getTexture ());
         gl .copyTexSubImage2D (this .getTarget (), 0, 0, 0, 0, 0, width, height);

         // Finish.

         this .updateTextureParameters ();

         if (this ._singleFrame .getValue ())
            this .update = false;
      };
   })(),
   dispose ()
   {
      Networking_X3DUrlOutputObject .prototype .dispose .call (this);
      Texturing_X3DTexture2DNode   .prototype .dispose .call (this);
   },
});

Object .defineProperties (RenderedTexture,
{
   ... Core_X3DNode .getStaticProperties ("RenderedTexture", "Texturing", 4, "texture", "4.1"),
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "metadata",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "description",       new x_ite_Fields .SFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "enabled",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "replaceImage",      new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "maxNumberFrames",   new x_ite_Fields .SFInt32 (1000)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "url",               new x_ite_Fields .MFString ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "singleFrame",       new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "updateInterval",    new x_ite_Fields .SFTime (0.1)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "width",             new x_ite_Fields .SFInt32 (128)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "height",            new x_ite_Fields .SFInt32 (128)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "colorDepth",        new x_ite_Fields .SFInt32 (4)),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "depthMap",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly,     "isActive",          new x_ite_Fields .SFBool ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatS",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "repeatT",           new x_ite_Fields .SFBool (true)),
         new Base_X3DFieldDefinition (Base_X3DConstants .initializeOnly, "textureProperties", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .inputOutput,    "children",          new x_ite_Fields .MFNode ()),
      ]),
      enumerable: true,
   },
});

const RenderedTexture_default_ = RenderedTexture;
;

/* harmony default export */ const Texturing_RenderedTexture = (Namespace/* default */.A .add ("RenderedTexture", RenderedTexture_default_));
;// ./src/x_ite/Components/TexturingComponent.js



















const TexturingComponent_default_ = {
   name: "Texturing",
   concreteNodes:
   [
      Texturing_ImageTexture,
      Texturing_MovieTexture,
      Texturing_MultiTexture,
      Texturing_MultiTextureCoordinate,
      Texturing_MultiTextureTransform,
      Texturing_PixelTexture,
      Texturing_RenderedTexture,
      Texturing_TextureCoordinate,
      Texturing_TextureCoordinateGenerator,
      Texturing_TextureProperties,
      Texturing_TextureTransform,
   ],
   abstractNodes:
   [
      Texturing_X3DSingleTextureCoordinateNode,
      Texturing_X3DSingleTextureNode,
      Texturing_X3DSingleTextureTransformNode,
      Texturing_X3DTexture2DNode,
      Texturing_X3DTextureCoordinateNode,
      Texturing_X3DTextureNode,
      Texturing_X3DTextureTransformNode,
   ],
};
;

/* harmony default export */ const TexturingComponent = (Namespace/* default */.A .add ("TexturingComponent", TexturingComponent_default_));
;// ./src/x_ite/Components/TimeComponent.js



const TimeComponent_default_ = {
   name: "Time",
   concreteNodes:
   [
      Time_TimeSensor,
   ],
   abstractNodes:
   [
      Time_X3DTimeDependentNode,
   ],
};
;

/* harmony default export */ const TimeComponent = (Namespace/* default */.A .add ("TimeComponent", TimeComponent_default_));
;// ./src/x_ite/Components.js




















let external = false;

const Components =
{
   add ({ name, concreteNodes, abstractNodes, browserContext })
   {
      Browser_X3DBrowserContext .addComponent ({ name, concreteNodes, abstractNodes, browserContext, external });
   },
};

Components .add (CoreComponent);
Components .add (EnvironmentalEffectsComponent);
Components .add (EnvironmentalSensorComponent);
Components .add (FollowersComponent);
Components .add (Geometry3DComponent);
Components .add (GroupingComponent);
Components .add (InterpolationComponent);
Components .add (LayeringComponent);
Components .add (LightingComponent);
Components .add (NavigationComponent);
Components .add (NetworkingComponent);
Components .add (PointingDeviceSensorComponent);
Components .add (RenderingComponent);
Components .add (ShadersComponent);
Components .add (ShapeComponent);
Components .add (SoundComponent);
Components .add (TexturingComponent);
Components .add (TimeComponent);

external = true;

const Components_default_ = Components;
;

/* harmony default export */ const x_ite_Components = (Namespace/* default */.A .add ("Components", Components_default_));
;// ./src/x_ite/Browser/X3DBrowser.js
/* provided dependency */ var X3DBrowser_$ = __webpack_require__(44).A;





























const
   _DOMIntegration      = Symbol (),
   _supportedProfiles   = Symbol (),
   _supportedComponents = Symbol (),
   _concreteNodes       = Symbol (),
   _abstractNodes       = Symbol (),
   _fieldTypes          = Symbol (),
   _reject              = Symbol (),
   X3DBrowser_fileLoader          = Symbol (),
   _browserCallbacks    = Symbol (),
   _console             = Symbol (),
   X3DBrowser_processEvents       = Symbol .for ("X_ITE.X3DRoutingContext.processEvents");

function X3DBrowser (element)
{
   element = X3DBrowser_$(element);

   if (element .prop ("browser"))
      throw new Error ("Couldn't create browser, element has already a browser.");

   Browser_X3DBrowserContext .call (this, element);

   this .addType (Base_X3DConstants .X3DBrowser);

   this [_supportedProfiles]   = Configuration_SupportedProfiles .copy ();
   this [_supportedComponents] = Configuration_SupportedComponents .copy ();
   this [_concreteNodes]       = ConcreteNodes .copy ();
   this [_abstractNodes]       = AbstractNodes .copy ();
   this [_fieldTypes]          = FieldTypes .copy ();
   this [_console]             = document .getElementsByClassName ("x_ite-console");

   this [_browserCallbacks] = new Map ([
      [Base_X3DConstants .CONNECTION_ERROR,  new Map ()],
      [Base_X3DConstants .BROWSER_EVENT,     new Map ()],
      [Base_X3DConstants .INITIALIZED_EVENT, new Map ()],
      [Base_X3DConstants .SHUTDOWN_EVENT,    new Map ()],
      [Base_X3DConstants .INITIALIZED_ERROR, new Map ()],
   ]);

   this .setup ();
};

Object .assign (Object .setPrototypeOf (X3DBrowser .prototype, Browser_X3DBrowserContext .prototype),
{
   initialize ()
   {
      Browser_X3DBrowserContext .prototype .initialize .call (this);

      this .getCanvas () .on ("webglcontextlost", event =>
      {
         this .callBrowserCallbacks (Base_X3DConstants .CONNECTION_ERROR);
      });

      const scene = new Execution_X3DScene (this);

      scene .setup ();

      this .replaceWorld (scene)
         .catch (DEVELOPMENT ? error => console .error (error) : Function .prototype);

      this [_DOMIntegration] = new Browser_DOMIntegration (this);

      Browser_Legacy .browser (this);

      // Process events from context creation. This will setup nodes like
      // geometry option nodes before any node is created.
      this [X3DBrowser_processEvents] .call (this);

      // Print welcome message.

      if (this .getInstanceId () > 1)
         return;

      if (!this .getBrowserOption ("Debug"))
         return;

      this .print (this .getWelcomeMessage ());
   },
   getWelcomeMessage ()
   {
      return `Welcome to ${this .name} X3D Browser v${this .version}:\n` +
             `   Current Graphics Renderer\n` +
             `      Name: ${this .getVendor ()} ${this .getRenderer ()}\n` +
             `      WebGL version: ${this .getWebGLVersion ()}\n` +
             `      Shading language: ${this .getShadingLanguageVersion ()}\n` +
             `      WebXR: ${"xr" in navigator}\n` +
             `   Rendering Properties\n` +
             `      ContentScale: ${this .getRenderingProperty ("ContentScale")}\n` +
             `      Antialiased: ${this .getRenderingProperty ("Antialiased")}\n` +
             `      Max samples: ${this .getMaxSamples ()}\n` +
             `      Depth size: ${this .getDepthSize ()} bits\n` +
             `      Color depth: ${this .getColorDepth ()} bits\n` +
             `      Max clip planes per shape: ${this .getMaxClipPlanes ()}\n` +
             `      Max lights per shape: ${this .getMaxLights ()}\n` +
             `      Max multi textures per shape: ${this .getMaxTextures ()}\n` +
             `      Max texture size: ${this .getMaxTextureSize ()} × ${this .getMaxTextureSize ()} pixels\n` +
             `      Texture memory: ${this .getTextureMemory () || "n/a"}\n` +
             `      Texture units: ${this .getMaxCombinedTextureUnits ()}\n` +
             `      Max vertex uniform vectors: ${this .getMaxVertexUniformVectors ()}\n` +
             `      Max fragment uniform vectors: ${this .getMaxFragmentUniformVectors ()}\n` +
             `      Max vertex attribs: ${this .getMaxVertexAttribs ()}\n` +
             `      Max varying vectors: ${this .getMaxVaryingVectors ()}\n`;
   },
   getName ()
   {
      return "X_ITE";
   },
   getVersion ()
   {
      return BROWSER_VERSION;
   },
   getDescription ()
   {
      return this .getNotification () ._string .getValue ()
   },
   setDescription (value)
   {
      this .getNotification () ._string = value;
   },
   getWorldURL ()
   {
      return this .currentScene .worldURL;
   },
   getProfile (name)
   {
      const profile = this [_supportedProfiles] .get (String (name));

      if (profile)
         return profile;

      throw Error (`Profile '${name}' is not supported.`);
   },
   addSupportedProfile: function (profile)
   {
      this [_supportedProfiles] .add (profile .name, profile);
   },
   updateSupportedProfile: function (profile)
   {
      this [_supportedProfiles] .update (profile .name, profile .name, profile);
   },
   removeSupportedProfile (name)
   {
      return this [_supportedProfiles] .remove (String (name));
   },
   getSupportedProfile (name)
   {
      const profile = this [_supportedProfiles] .get (String (name));

      if (profile)
         return profile;

      throw new Error (`Supported profile '${name}' does not exists.`);
   },
   getSupportedProfiles ()
   {
      return this [_supportedProfiles];
   },
   getComponent (name, level)
   {
      name   = String (name);
      level |= 0;

      const component = this [_supportedComponents] .get (name);

      if (component)
      {
         return new Configuration_ComponentInfo (component .name,
            Math_Algorithm .clamp (level || component .level, 1, component .level),
            component .title,
            component .providerURL,
            component .external,
            component .dependencies);
      }

      throw Error (`Component '${name}' at level '${level}' is not supported.`);
   },
   addSupportedComponent (component)
   {
      this [_supportedComponents] .add (component .name, component);
   },
   updateSupportedComponent (component)
   {
      this [_supportedComponents] .update (component .name, component .name, component);
   },
   removeSupportedComponent (name)
   {
      this [_supportedComponents] .remove (String (name));
   },
   getSupportedComponent (name)
   {
      const component = this [_supportedComponents] .get (String (name));

      if (component)
         return component;

      throw new Error (`Supported component '${name}' does not exists.`);
   },
   getSupportedComponents ()
   {
      return this [_supportedComponents];
   },
   loadComponents: (() =>
   {
      function loadComponents (components, seen)
      {
         return Promise .all (components .map (component => loadComponent .call (this, component, seen)));
      }

      async function loadComponent ({ name, providerURL, external, dependencies }, seen)
      {
         if (seen .has (name)) return; seen .add (name);

         await loadComponents .call (this, dependencies .map (name => this [_supportedComponents] .get (name)), seen);

         if (!external)
            return;

         switch (x_ite_Features .ENVIRONMENT)
         {
            case "NODE":
            {
               __webpack_require__.g .require (__webpack_require__.g .require ("url") .fileURLToPath (providerURL));
               break;
            }
            case "BROWSER":
            case "MODULE":
            {
               await import (/* webpackIgnore: true */ providerURL);
               break;
            }
         }
      }

      return function (... args)
      {
         const component = [ ];

         for (const arg of args)
         {
            if (arg instanceof Execution_X3DScene)
               component .push (... (arg .profile ?? this .getProfile ("Full")) .components, ... arg .components);

            else if (arg instanceof Configuration_ProfileInfo)
               component .push (... arg .components);

            else if (arg instanceof Configuration_ComponentInfoArray)
               component .push (... arg);

            else if (arg instanceof Configuration_ComponentInfo)
               component .push (arg);

            else if (typeof arg === "string")
               component .push (this .getComponent (arg))
         }

         // Load array of component names.
         return loadComponents .call (this, component, new Set ());
      };
   })(),
   addConcreteNode (ConcreteNode)
   {
      this [_concreteNodes] .add (ConcreteNode .typeName, ConcreteNode);
   },
   updateConcreteNode (ConcreteNode)
   {
      this [_concreteNodes] .update (ConcreteNode .typeName, ConcreteNode .typeName, ConcreteNode);
   },
   removeConcreteNode (ConcreteNode)
   {
      this [_concreteNodes] .remove (ConcreteNode .typeName);
   },
   getConcreteNode (typeName)
   {
      const concreteNode = this [_concreteNodes] .get (String (typeName));

      if (concreteNode)
         return concreteNode;

      throw new Error (`Concrete node '${typeName}' does not exists.`);
   },
   getConcreteNodes ()
   {
      return this [_concreteNodes];
   },
   addAbstractNode (AbstractNode)
   {
      this [_abstractNodes] .add (AbstractNode .typeName, AbstractNode);
   },
   updateAbstractNode (AbstractNode)
   {
      this [_abstractNodes] .update (AbstractNode .typeName, AbstractNode .typeName, AbstractNode);
   },
   removeAbstractNode (AbstractNode)
   {
      this [_abstractNodes] .remove (AbstractNode .typeName);
   },
   getAbstractNode (typeName)
   {
      const abstractNode = this [_abstractNodes] .get (String (typeName));

      if (abstractNode)
         return abstractNode;

      throw new Error (`Abstract node '${typeName}' does not exists.`);
   },
   getAbstractNodes ()
   {
      return this [_abstractNodes];
   },
   getFieldType (typeName)
   {
      const fieldType = this [_fieldTypes] .get (String (typeName));

      if (fieldType)
         return fieldType;

      throw new Error (`Field type '${typeName}' does not exists.`);
   },
   getFieldTypes ()
   {
      return this [_fieldTypes];
   },
   async createScene (profile, ... components)
   {
      const scene = new Execution_X3DScene (this);

      if (arguments .length)
      {
         if (!(profile instanceof Configuration_ProfileInfo))
            throw new Error ("Couldn't create scene: profile must be of type ProfileInfo.");

         scene .setProfile (profile);

         for (const component of components)
         {
            if (!(component instanceof Configuration_ComponentInfo))
               throw new Error ("Couldn't create scene: component must be of type ComponentInfo.");

            scene .updateComponent (component);
         }
      }

      scene .setup ();
      scene .setLive (true);

      await this .loadComponents (scene);

      return scene;
   },
   replaceWorld (scene)
   {
      return new Promise (async (resolve, reject) =>
      {
         this [X3DBrowser_fileLoader] ?.abort ();

         this [_reject] ?.("Replacing world aborted.");
         this [_reject] = reject;

         this .setBrowserLoading (true);

         // Remove world.

         if (this .getWorld ())
         {
            // Wait for events to be processed before scene is replaced, to get correct
            // results from getBBox and viewpoint binding in new scene, especially when
            // a SCRIPTED scene is created.
            await this .nextFrame ();

            if (this [_reject] !== reject)
               return;

            this .getExecutionContext () .setLive (false);
            this .shutdown () .processInterests ();
            this .callBrowserCallbacks (Base_X3DConstants .SHUTDOWN_EVENT);
            this .callBrowserEventHandler ("shutdown");

            // There could be a replaceWorld from a shutdown event handlers,
            // so we need to check if the reject function is still the same.
            if (this [_reject] !== reject)
               return;
         }

         // Replace world.

         if (scene instanceof x_ite_Fields .MFNode)
         {
            // VRML version of replaceWorld has a MFNode value as argument.

            const rootNodes = scene;

            scene = new Execution_X3DScene (this);

            for (const node of rootNodes .filter (node => node))
               scene .getLive () .addInterest ("setLive", node .getValue () .getExecutionContext ());

            scene .setRootNodes (rootNodes);
            scene .setup ();
         }

         if (!(scene instanceof Execution_X3DScene))
         {
            scene = new Execution_X3DScene (this);
            scene .setup ();
         }

         // Detach scene from parent.

         scene .setExecutionContext (null);
         scene .setLive (true);

         // Replace.

         this .setExecutionContext (scene);
         this .setDescription ("");

         this ._loadCount .addInterest ("checkLoadCount", this, resolve, reject);
         this ._loadCount .addEvent ();
      });
   },
   checkLoadCount (resolve, reject, loadCount)
   {
      if (this [_reject] !== reject)
         return;

      if (loadCount .getValue ())
         return;

      loadCount .removeInterest ("checkLoadCount", this);
      this .setBrowserLoading (false);
      this .initialized () .set (this .getCurrentTime ());
      this .initialized () .processInterests ();
      this .callBrowserCallbacks (Base_X3DConstants .INITIALIZED_EVENT);
      this .callBrowserEventHandler ("initialized load");
      resolve ();
   },
   createVrmlFromString (vrmlSyntax)
   {
      const
         external     = this .isExternal (),
         currentScene = this .currentScene,
         worldURL     = external ? this .getBaseURL () : currentScene .getWorldURL (),
         fileLoader   = new InputOutput_FileLoader (this .getScriptNode () ?? this .getWorld ()),
         scene        = fileLoader .createX3DFromString (worldURL, `#VRML V2.0 utf8\n\n${vrmlSyntax}`);

      if (!external)
         scene .setExecutionContext (currentScene);

      scene .setLive (true);

      return scene .rootNodes;
   },
   async createX3DFromString (x3dSyntax)
   {
      x3dSyntax = String (x3dSyntax);

      const
         external     = this .isExternal (),
         currentScene = this .currentScene,
         worldURL     = external ? this .getBaseURL () : currentScene .getWorldURL (),
         fileLoader   = new InputOutput_FileLoader (this .getScriptNode () ?? this .getWorld ());

      const scene = await new Promise ((resolve, reject) =>
      {
         fileLoader .createX3DFromString (worldURL, x3dSyntax, resolve, reject);
      });

      if (!external)
         scene .setExecutionContext (currentScene);

      scene .setLive (true);

      return scene;
   },
   createVrmlFromURL (url, node, event)
   {
      node  = Base_X3DCast (Base_X3DConstants .X3DNode, node, false);
      event = String (event);

      if (!(url instanceof x_ite_Fields .MFString))
         throw new Error ("Browser.createVrmlFromURL: url must be of type MFString.");

      if (!node)
         throw new Error ("Browser.createVrmlFromURL: node must be of type X3DNode.");

      const field = node .getField (event);

      if (!field .isInput ())
         throw new Error (`Browser.createVrmlFromURL: event named '${event}' must be a input field.`);

      if (field .getType () !== Base_X3DConstants .MFNode)
         throw new Error (`Browser.createVrmlFromURL: event named '${event}' must be of type MFNode.`);

      const
         currentScene = this .currentScene,
         external     = this .isExternal (),
         fileLoader   = new InputOutput_FileLoader (this .getScriptNode () ?? this .getWorld ());

      this .addLoadingObject (fileLoader);

      fileLoader .createX3DFromURL (url, null, scene =>
      {
         this .removeLoadingObject (fileLoader);

         if (!scene)
            return;

         // Handle getLive for script scenes here:

         if (!external)
            scene .setExecutionContext (currentScene);

         scene .setLive (true);

         // Wait until scene is completely loaded, scene ._loadCount must be 0.
         field .setValue (scene .rootNodes);
      });
   },
   createX3DFromURL (url, node, event)
   {
      if (arguments .length === 3)
         return this .createVrmlFromURL (url, node, event);

      // arguments .length === 1

      if (!(url instanceof x_ite_Fields .MFString))
         throw new Error ("Browser.createX3DFromURL: url must be of type MFString.");

      return new Promise ((resolve, reject) =>
      {
         const
            currentScene = this .currentScene,
            external     = this .isExternal (),
            fileLoader   = new InputOutput_FileLoader (this .getScriptNode () ?? this .getWorld ());

         this .addLoadingObject (fileLoader);

         fileLoader .createX3DFromURL (url, null, scene =>
         {
            this .removeLoadingObject (fileLoader);

            if (scene)
            {
               if (!external)
                  scene .setExecutionContext (currentScene);

               scene .setLive (true);

               resolve (scene);
            }
            else
            {
               reject (new Error ("Couldn't load X3D file."));
            }
         })
      });
   },
   loadURL (url, parameter = new x_ite_Fields .MFString ())
   {
      return new Promise ((resolve, reject) =>
      {
         if (!(url instanceof x_ite_Fields .MFString))
            throw new Error ("Browser.loadURL: url must be of type MFString.");

         if (!(parameter instanceof x_ite_Fields .MFString))
            throw new Error ("Browser.loadURL: parameter must be of type MFString.");

         // Start loading.

         const fileLoader = new InputOutput_FileLoader (this .getScriptNode () ?? this .getWorld ());

         fileLoader .createX3DFromURL (url, parameter, scene =>
         {
            fileLoader .ready = true;

            if (this [X3DBrowser_fileLoader] !== fileLoader)
            {
               this .removeLoadingObject (fileLoader);
               reject (new Error ("Loading of X3D file aborted."));
            }
            else
            {
               if (!this .getBrowserOption ("SplashScreen"))
                  this .getCanvas () .show ();

               if (scene)
               {
                  this .addLoadingObject (this); // Prevent resetLoadCount.
                  this .replaceWorld (scene) .then (resolve) .catch (reject);
                  this .removeLoadingObject (this);
                  this .removeLoadingObject (fileLoader);
               }
               else
               {
                  this .callBrowserCallbacks (Base_X3DConstants .INITIALIZED_ERROR);
                  this .callBrowserEventHandler ("error");

                  setTimeout (() =>
                  {
                     this .getSplashScreen ()
                        .find (".x_ite-private-spinner-text")
                        .text (gettext ("Failed loading world."));
                  });

                  reject (new Error ("Couldn't load X3D file."));
               }
            }
         },
         (fragment) =>
         {
            fileLoader .ready = true;

            this .changeViewpoint (fragment);
            this .removeLoadingObject (fileLoader);

            resolve ();
         },
         (url, target) =>
         {
            fileLoader .ready = true;

            if (target)
               window .open (url, target);
            else
               location = url;

            this .removeLoadingObject (fileLoader);

            resolve ();
         });

         if (!fileLoader .ready)
         {
            this [X3DBrowser_fileLoader] ?.abort ();

            this .setBrowserLoading (true);
            this .addLoadingObject (this [X3DBrowser_fileLoader] = fileLoader);
         }
      });
   },
   addBrowserListener (callback, object)
   {
      // The string describes the name of the callback function to be called within the current ECMAScript context.
   },
   removeBrowserListener (callback)
   {
      // The string describes the name of the callback function to be called within the current ECMAScript context.
   },
   addBrowserCallback (... args)
   {
      switch (args .length)
      {
         case 2:
         {
            const [key, object] = args;

            for (const [event, map] of this [_browserCallbacks])
               this [_browserCallbacks] .set (event, new Map (map) .set (key, object));

            break;
         }
         case 3:
         {
            const
               [key, event, object] = args,
               map                  = new Map (this [_browserCallbacks] .get (event));

            this [_browserCallbacks] .set (event, map);

            map .set (key, object);
            break;
         }
      }
   },
   removeBrowserCallback (... args)
   {
      switch (args .length)
      {
         case 1:
         {
            const [key] = args;

            for (const map of this [_browserCallbacks] .values ())
               map .delete (key);

            break;
         }
         case 2:
         {
            const [key, event] = args;

            this [_browserCallbacks] .get (event) .delete (key);
            break;
         }
      }
   },
   getBrowserCallbacks (event)
   {
      if (arguments .length === 1)
         return this [_browserCallbacks] .get (event);
      else
         return new Map ([... this [_browserCallbacks]] .flatMap (([event, map]) => [... map]));
   },
   callBrowserCallbacks (event)
   {
      for (const callback of this [_browserCallbacks] .get (event) .values ())
         callback (event);
   },
   async importDocument (dom)
   {
      const
         currentScene = this .currentScene,
         scene        = new Execution_X3DScene (this),
         external     = this .isExternal ();

      const parser = new Parser_XMLParser (scene);

      parser .setInput (dom)

      await new Promise (parser .parseIntoScene .bind (parser));

      if (!external)
         scene .setExecutionContext (currentScene);

      scene .isLive (true);

      return scene;
   },
   async importJS (json)
   {
      const
         currentScene = this .currentScene,
         scene        = new Execution_X3DScene (this),
         external     = this .isExternal ();

      const parser = new Parser_JSONParser (scene);

      parser .setInput (json);

      await new Promise (parser .parseIntoScene .bind (parser));

      if (!external)
         scene .setExecutionContext (currentScene);

      scene .isLive (true);

      return scene;
   },
   getBrowserProperty (name)
   {
      return this .getBrowserProperties () .getField (name) .getValue ();
   },
   setBrowserOption (name, value)
   {
      this .getBrowserOptions () .getField (name) .setValue (value);
   },
   getBrowserOption (name)
   {
      return this .getBrowserOptions () .getField (name) .getValue ();
   },
   getRenderingProperty (name)
   {
      return this .getRenderingProperties () .getField (name) .getValue ();
   },
   viewAll (layerNode, transitionTime = 1)
   {
      if (arguments .length === 1 && typeof layerNode === "number")
         transitionTime = layerNode;

      layerNode      = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();
      transitionTime = +transitionTime;

      layerNode ?.viewAll (transitionTime, 1, this .getBrowserOption ("StraightenHorizon"));
   },
   firstViewpoint (layerNode)
   {
      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const viewpoints = layerNode ?.getUserViewpoints ();

      if (viewpoints ?.length)
         this .bindViewpoint (layerNode, viewpoints .at (0));
   },
   previousViewpoint (layerNode)
   {
      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const viewpoints = layerNode ?.getUserViewpoints ();

      if (!viewpoints ?.length)
         return;

      const i = viewpoints .findIndex (viewpointNode => viewpointNode ._isBound .getValue ());

      this .bindViewpoint (layerNode, viewpoints .at (i > -1 ? i - 1 : -1));
   },
   nextViewpoint (layerNode)
   {
      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const viewpoints = layerNode ?.getUserViewpoints ();

      if (!viewpoints ?.length)
         return;

      const i = viewpoints .findIndex (viewpointNode => viewpointNode ._isBound .getValue ());

      this .bindViewpoint (layerNode, viewpoints .at ((i + 1) % viewpoints .length));
   },
   lastViewpoint (layerNode)
   {
      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const viewpoints = layerNode ?.getUserViewpoints ();

      if (viewpoints ?.length)
         this .bindViewpoint (layerNode, viewpoints .at (-1));
   },
   changeViewpoint (layerNode, name)
   {
      if (arguments .length === 1)
      {
         name      = layerNode;
         layerNode = this .getActiveLayer ();
      }

      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();
      name      = String (name);

      const viewpointNode = layerNode ?.getViewpoints () .get ()
         .find (viewpointNode => viewpointNode .getName () === name);

      if (viewpointNode)
         this .bindViewpoint (layerNode, viewpointNode);
   },
   bindViewpoint (layerNode, viewpointNode)
   {
      layerNode     = Base_X3DCast (Base_X3DConstants .X3DLayerNode,     layerNode);
      viewpointNode = Base_X3DCast (Base_X3DConstants .X3DViewpointNode, viewpointNode);

      if (!layerNode)
         throw new Error ("Browser.bindViewpoint: layerNode must be of type X3DLayerNode.")

      if (!viewpointNode)
         throw new Error ("Browser.bindViewpoint: viewpointNode must be of type X3DViewpointNode.")

      viewpointNode .setVRMLTransition (true);

      viewpointNode ._set_bind = true;
   },
   addRoute (sourceNode, sourceField, destinationNode, destinationField)
   {
      this .currentScene .addRoute (sourceNode, sourceField, destinationNode, destinationField);
   },
   deleteRoute (sourceNode, sourceField, destinationNode, destinationField)
   {
      this .currentScene .deleteRoute (sourceNode, sourceField, destinationNode, destinationField);
   },
   constrainTranslation (layerNode, translation)
   {
      if (arguments .length === 1)
      {
         translation = layerNode;
         layerNode   = this .getActiveLayer ();
      }

      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const constrained = layerNode ?.constrainTranslation (translation .getValue ());

      return constrained ? new x_ite_Fields .SFVec3f (... constrained) : null;
   },
   getClosestObject (layerNode, direction)
   {
      if (arguments .length === 1)
      {
         direction = layerNode;
         layerNode = this .getActiveLayer ();
      }

      layerNode = Base_X3DCast (Base_X3DConstants .X3DLayerNode, layerNode) ?? this .getActiveLayer ();

      const closestObject = layerNode ?.getClosestObject (direction .getValue ());

      return closestObject ?.node
      ? {
         node: Fields_SFNodeCache .get (closestObject .node),
         distance: closestObject .distance,
         normal: new x_ite_Fields .SFVec3f (... closestObject .normal),
      }
      : {
         node: null,
         distance: Infinity,
         normal: null,
      };
   },
   beginUpdate ()
   {
      this .setLive (true);
      this .advanceOnlyTime ();
      this .addBrowserEvent ();
   },
   endUpdate ()
   {
      this .getViewer () .disconnect ();
      this .setLive (false);
   },
   print (... args)
   {
      const string = args .map (String) .join (" ");

      console .log (string);

      for (const element of this [_console])
         element .append (document .createTextNode (string));
   },
   println (... args)
   {
      const string = args .map (String) .join (" ");

      console .log (string);

      for (const element of this [_console])
         element .append (document .createTextNode (`${string}\n`));
   },
   toVRMLStream (generator)
   {
      this .currentScene .toVRMLStream (generator);
   },
   toXMLStream (generator)
   {
      this .currentScene .toXMLStream (generator);
   },
   toJSONStream (generator)
   {
      this .currentScene .toJSONStream (generator);
   },
   loseContext ()
   {
      this .getContext () .getExtension ("WEBGL_lose_context") .loseContext ();
   },
   dispose ()
   {
      this [_DOMIntegration] .dispose ();

      Browser_X3DBrowserContext .prototype .dispose .call (this);
   },
});

for (const key of Object .keys (X3DBrowser .prototype))
   Object .defineProperty (X3DBrowser .prototype, key, { enumerable: false });

Object .defineProperties (X3DBrowser .prototype,
{
   name:
   {
      get: X3DBrowser .prototype .getName,
      enumerable: true,
   },
   version:
   {
      value: BROWSER_VERSION,
      enumerable: true,
   },
   scriptURL:
   {
      get: X3DBrowser .prototype .getScriptURL,
      enumerable: true,
   },
   providerURL:
   {
      get: X3DBrowser .prototype .getProviderURL,
      enumerable: true,
   },
   providerUrl: // legacy
   {
      get: X3DBrowser .prototype .getProviderURL,
      enumerable: false,
   },
   currentFrameRate:
   {
      get: X3DBrowser .prototype .getCurrentFrameRate,
      enumerable: true,
   },
   currentSpeed:
   {
      get: X3DBrowser .prototype .getCurrentSpeed,
      enumerable: true,
   },
   description:
   {
      get: X3DBrowser .prototype .getDescription,
      set: X3DBrowser .prototype .setDescription,
      enumerable: true,
   },
   baseURL:
   {
      get: X3DBrowser .prototype .getBaseURL,
      set: X3DBrowser .prototype .setBaseURL,
      enumerable: true,
   },
   supportedProfiles:
   {
      get: X3DBrowser .prototype .getSupportedProfiles,
      enumerable: true,
   },
   supportedComponents:
   {
      get: X3DBrowser .prototype .getSupportedComponents,
      enumerable: true,
   },
   concreteNodes:
   {
      get: X3DBrowser .prototype .getConcreteNodes,
      enumerable: true,
   },
   abstractNodes:
   {
      get: X3DBrowser .prototype .getAbstractNodes,
      enumerable: true,
   },
   fieldTypes:
   {
      get: X3DBrowser .prototype .getFieldTypes,
      enumerable: true,
   },
   currentScene:
   {
      get ()
      {
         return this .getScriptNode () ?.getExecutionContext () ?? this .getExecutionContext ();
      },
      enumerable: true,
   },
   activeLayer:
   {
      get ()
      {
         if (this .getActiveLayer () === this .getWorld () .getLayer0 ())
            return null;

         return this ._activeLayer .valueOf ();
      },
      enumerable: true,
   },
   activeNavigationInfo:
   {
      get ()
      {
         if (this .getActiveNavigationInfo () === this .getActiveLayer () ?.getNavigationInfoStack () .get () [0])
            return null;

         return this ._activeNavigationInfo .valueOf ();
      },
      enumerable: true,
   },
   activeViewpoint:
   {
      get ()
      {
         if (this .getActiveViewpoint () === this .getActiveLayer () ?.getViewpointStack () .get () [0])
            return null;

         return this ._activeViewpoint .valueOf ();
      },
      enumerable: true,
   },
   contextMenu:
   {
      get: X3DBrowser .prototype .getContextMenu,
      enumerable: true,
   },
   element:
   {
      get ()
      {
         return this .getElement () [0];
      },
      enumerable: true,
   },
});

Object .defineProperties (X3DBrowser,
{
   fieldDefinitions:
   {
      value: new Base_FieldDefinitionArray ([
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "activeLayer",          new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "activeNavigationInfo", new x_ite_Fields .SFNode ()),
         new Base_X3DFieldDefinition (Base_X3DConstants .outputOnly, "activeViewpoint",      new x_ite_Fields .SFNode ()),
      ]),
      enumerable: true,
   },
   typeName:
   {
      value: "X3DBrowser",
      enumerable: true,
   },
});

Base_X3DConstants .addConstant (X3DBrowser .typeName);

const X3DBrowser_default_ = X3DBrowser;
;

/* harmony default export */ const Browser_X3DBrowser = (Namespace/* default */.A .add ("X3DBrowser", X3DBrowser_default_));
;// ./src/x_ite/SUPPORTED_VERSIONS.js
// All supported X3D specification version:

const SUPPORTED_VERSIONS_default_ = [
   "2.0",
   "3.0",
   "3.1",
   "3.2",
   "3.3",
   "4.0",
   "4.1",
];
;

/* harmony default export */ const SUPPORTED_VERSIONS = (Namespace/* default */.A .add ("SUPPORTED_VERSIONS", SUPPORTED_VERSIONS_default_));
;// ./src/x_ite/X3DCanvasElement.js
/* provided dependency */ var X3DCanvasElement_$ = __webpack_require__(44).A;


class X3DCanvasElement extends HTMLElement
{
   browser;

   constructor ()
   {
      try
      {
         super ();

         new Browser_X3DBrowser (this);
      }
      catch (error)
      {
         console .error (error);

         X3DCanvasElement_$(this .shadowRoot)
            .append (X3DCanvasElement_$("<slot></slot>"))
            .children (".x_ite-private-browser") .remove ();
      }
   }

   connectedCallback ()
   {
      this .browser ?.connectedCallback ();
   }

   disconnectedCallback ()
   {
      this .browser ?.disconnectedCallback ();
   }

   static get observedAttributes ()
   {
      const attributes = [
         "antialiased",
         "baseURL",
         "cache",
         "colorSpace",
         "contentScale",
         "contextMenu",
         "debug",
         "displayColorSpace",
         "exposure",
         "extensions",
         "logarithmicDepthBuffer",
         "maximumFrameRate",
         "multisampling",
         "notifications",
         "oninitialized",
         "onshutdown",
         "orderIndependentTransparency",
         "splashScreen",
         "src",
         "textCompression",
         "timings",
         "toneMapping",
         "update",
         "url",
         "xrSessionMode",
      ];

      return attributes .concat (attributes .map (attribute => attribute .toLowerCase ()));
   }

   attributeChangedCallback (name, oldValue, newValue)
   {
      this .browser ?.attributeChangedCallback (name, oldValue, newValue);
   }

   captureStream (... args)
   {
      return this .browser ?.getCanvas () [0] .captureStream (... args);
   }

   toBlob (... args)
   {
      return this .browser ?.getCanvas () [0] .toBlob (... args);
   }

   toDataURL (... args)
   {
      return this .browser ?.getCanvas () [0] .toDataURL (... args);
   }

   get [Symbol .toStringTag] ()
   {
      return "X3DCanvasElement";
   }
}

// IE fix.
document .createElement ("X3DCanvas");

const X3DCanvasElement_default_ = X3DCanvasElement;
;

/* harmony default export */ const x_ite_X3DCanvasElement = (Namespace/* default */.A .add ("X3DCanvasElement", X3DCanvasElement_default_));
;// ./src/standard/Math/Algorithms/QuickSort.js
function QuickSort (array, compare)
{
   this .array = array;

   if (compare)
      this .compare = compare;
}

Object .assign (QuickSort .prototype,
{
   compare (lhs, rhs)
   {
      return lhs < rhs;
   },
   sort (first, last)
   {
      if (last - first > 1)
         this .quicksort (first, last - 1);
   },
   quicksort (lo, hi)
   {
      let
         i = lo,
         j = hi;

      const { array, compare } = this;

      // Vergleichs­element x
      const x = array [(lo + hi) >>> 1];

      for (;;)
      {
         while (compare (array [i], x)) ++ i;
         while (compare (x, array [j])) -- j;

         if (i < j)
         {
            // Exchange

            const t = array [i];
            array [i] = array [j];
            array [j] = t;

            i ++; j --;
         }
         else
         {
            if (i === j) ++ i, -- j;
            break;
         }
      }

      // Rekursion
      if (lo < j) this .quicksort (lo, j);
      if (i < hi) this .quicksort (i, hi);
   },
});

const QuickSort_default_ = QuickSort;
;

/* harmony default export */ const Algorithms_QuickSort = (Namespace/* default */.A .add ("QuickSort", QuickSort_default_));
;// ./src/lib/jquery.js
/* provided dependency */ var jquery_$ = __webpack_require__(44).A;
Object .assign (jquery_$,
{
   decodeText (input)
   {
      if (typeof input === "string")
         return input;

      return new TextDecoder () .decode (input);
   },
   sleep (ms)
   {
      return new Promise (resolve => setTimeout (resolve, ms));
   },
   toLowerCaseFirst (string)
   {
      return string [0] .toLowerCase () + string .slice (1);
   },
   toUpperCaseFirst (string)
   {
      return string [0] .toUpperCase () + string .slice (1);
   },
   try (callback, logError = false)
   {
      try
      {
         return callback ();
      }
      catch (error)
      {
         if (logError)
            console .error (error);
      }
   },
   /**
    * Decompresses an ArrayBuffer or Blob and returns an ArrayBuffer.
    *
    * @param {ArrayBuffer | Blob} data
    * @returns ArrayBuffer
    */
   async gunzip (data)
   {
      const
         blob  = data instanceof Blob ? data : new Blob ([data]),
         magic = await blob .slice (0, 2) .arrayBuffer ();

      if (!jquery_$.isGzip (magic))
         return await blob .arrayBuffer ();

      try
      {
         const
            inputStream  = blob .stream (),
            outputStream = inputStream .pipeThrough (new DecompressionStream ("gzip"));

         return await new Response (outputStream) .arrayBuffer ();
      }
      catch
      {
         return await blob .arrayBuffer ();
      }
   },
   isGzip (arrayBuffer)
   {
      if (arrayBuffer .byteLength < 2)
         return false;

      const bytes = new Uint8Array (arrayBuffer);

      return bytes [0] === 0x1f && bytes [1] === 0x8b;
   },
});

// // decorator: @iffe fn (... args) { return function () { }; }
// function iife (target, key, descriptor)
// {
//    descriptor .value = descriptor .value ();
//
//    return descriptor;
// }
//
// class C {
//   @iffe fn (... args) { return function () { }; }
// }

const jquery_default_ = jquery_$;
;

/* harmony default export */ const jquery = (Namespace/* default */.A .add ("jquery", jquery_default_));
;// ./src/lib/libtess.js
/* provided dependency */ var libtess_libtess = __webpack_require__(872);
const libtess_default_ = libtess_libtess;
;

/* harmony default export */ const lib_libtess = (Namespace/* default */.A .add ("libtess", libtess_default_));
;// ./src/x_ite/X3D.js
/* provided dependency */ var X3D_$ = __webpack_require__(44).A;








































let promise; // Declare return value of X3D function.

/**
*
* @param {function?} onfulfilled
* @param {function?} onrejected
* @returns {Promise<void>} Promise
*/
const X3D = Object .assign (function (onfulfilled, onrejected)
{
   promise ??= new Promise ((resolve, reject) =>
   {
      X3D_$(() =>
      {
         try
         {
            Browser_Legacy .elements (X3D_$("X3DCanvas"), Browser_X3DBrowser);

            if (Array .from (X3D_$("x3d-canvas")) .every (canvas => canvas .browser))
               resolve ();
            else
               throw new Error ("Couldn't create browser.");
         }
         catch (error)
         {
            Browser_Legacy .error (X3D_$("X3DCanvas"), error);
            reject (error);
         }
      });
   });

   return promise .then (onfulfilled) .catch (onrejected);
},
Namespace/* default */.A, Namespace/* default */.A .Fields,
{
   X3DConstants: Base_X3DConstants,
   X3DBrowser: Browser_X3DBrowser,
   X3DExecutionContext: Execution_X3DExecutionContext,
   X3DScene: Execution_X3DScene,
   ComponentInfo: Configuration_ComponentInfo,
   ComponentInfoArray: Configuration_ComponentInfoArray,
   ProfileInfo: Configuration_ProfileInfo,
   ProfileInfoArray: Configuration_ProfileInfoArray,
   ConcreteNodesArray: Configuration_ConcreteNodesArray,          // non-standard
   AbstractNodesArray: Configuration_AbstractNodesArray,          // non-standard
   UnitInfo: Configuration_UnitInfo,
   UnitInfoArray: Configuration_UnitInfoArray,
   NamedNodesArray: Execution_NamedNodesArray,             // non-standard
   ImportedNodesArray: Execution_ImportedNodesArray,          // non-standard
   X3DImportedNode: Execution_X3DImportedNode,             // non-standard
   ExportedNodesArray: Execution_ExportedNodesArray,          // non-standard
   X3DExportedNode: Execution_X3DExportedNode,             // non-standard
   ExternProtoDeclarationArray: Prototype_ExternProtoDeclarationArray,
   ProtoDeclarationArray: Prototype_ProtoDeclarationArray,
   X3DExternProtoDeclaration: Prototype_X3DExternProtoDeclaration,
   X3DProtoDeclaration: Prototype_X3DProtoDeclaration,
   X3DProtoDeclarationNode: Prototype_X3DProtoDeclarationNode,     // non-standard
   RouteArray: Routing_RouteArray,
   X3DRoute: Routing_X3DRoute,

   X3DBaseNode: Base_X3DBaseNode,                 // non-standard

   X3DFieldDefinition: Base_X3DFieldDefinition,
   FieldDefinitionArray: Base_FieldDefinitionArray,

   X3DField: Base_X3DField,
   X3DArrayField: Base_X3DArrayField,

   ... x_ite_Fields,
},
{
   /**
   * @deprecated Use X3D.ModuleName instead.
   */
   require (path)
   {
      return Namespace/* default */.A [path .match (/([^\/]{1,256})$/) ?.[1]];
   },
   noConflict: (() =>
   {
      const
         _had = window .hasOwnProperty ("X3D"),
         _X3D = window .X3D;

      return function ()
      {
         if (window .X3D === X3D)
         {
            if (_had)
               window .X3D = _X3D;
            else
               delete window .X3D;
         }

         return X3D;
      };
   })(),
   getBrowser (element)
   {
      return X3D_$(element || "x3d-canvas, X3DCanvas") .filter ("x3d-canvas, X3DCanvas") .prop ("browser");
   },
   createBrowser (... args)
   {
      const element = document .createElement ("x3d-canvas");

      if (args .length)
         element .browser .loadURL (... args);

      return element;
   },
   toString ()
   {
      return Object .prototype .toString .call (this);
   },
   [Symbol .toStringTag]: "X3D",
});

/* harmony default export */ const x_ite_X3D = (X3D);

;// ./src/x_ite.js



// Assign X3D to global namespace.

window [Symbol .for ("X_ITE.X3D")] = x_ite_X3D;

// Activate extensions.

function activateExtensions (X3D)
{
   const
      _extensions = Symbol .for ("X_ITE.extensions"),
      extensions  = window [_extensions];

   if (Array .isArray (extensions))
   {
      for (const extension of extensions)
         extension (X3D);
   }

   delete window [_extensions];
}

activateExtensions (x_ite_X3D);

// Define <x3d-canvas> element.

customElements .define ("x3d-canvas", x_ite_X3DCanvasElement);

x_ite_X3D ();

/* harmony default export */ const x_ite = (x_ite_X3D);

export { x_ite as default };
