/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
'use strict';

var index = require('./index-C_J7n4Dv.js');
var bundle = require('./bundle-BhTmftd-.js');
var bundle$1 = require('./bundle-96sYCpzz.js');
var multiElementInteractionHandler = require('./multiElementInteractionHandler-CV_R_zWs.js');
require('./streamAttributes-ce88H9XR.js');
require('./entities-BhIrCJqA.js');
var viewport = require('./viewport-ZSVJIQTY.js');
require('./_commonjsHelpers-CBbBosIU.js');
require('./dom-CXWXs9Hx.js');
require('./bundle-Dsc8KJRQ.js');

const viewerDomElementCss = () => `:host{position:absolute;pointer-events:none;visibility:hidden}:host(.hydrated){visibility:hidden}:host(.ready){visibility:inherit}:host(:not([interactions-off]))>*{pointer-events:auto}`;

const ViewerDomElement = class {
    constructor(hostRef) {
        index.registerInstance(this, hostRef);
        this.occlusionStateChanged = index.createEvent(this, "occlusionStateChanged");
        this.detachedStateChanged = index.createEvent(this, "detachedStateChanged");
        this.propertyChange = index.createEvent(this, "propertyChange");
        /**
         * The local 3D position of where this element is located.
         */
        this.position = bundle.vector3.origin();
        /**
         * The local 3D position of where this element is located, as a JSON string.
         * JSON representation can either be in the format of `[x, y, z]` or `{"x": 0,
         * "y": 0, "z": 0}`.
         */
        this.positionJson = '';
        /**
         * The local rotation of this element.
         */
        this.quaternion = bundle.quaternion.create();
        /**
         * The local quaternion rotation of this element, as a JSON string. JSON
         * representation can either be `[x, y, z, w]` or `{"x": 0, "y": 0, "z":
         * 0, "w": 1}`.
         */
        this.quaternionJson = '';
        /**
         * The local scale of this element.
         */
        this.scale = bundle.vector3.create(1, 1, 1);
        /**
         * The local scale of this element, as a JSON string. JSON string
         * representation can either be in the format of `[x, y, z]` or `{"x": 0, "y":
         * 0, "z": 0}`.
         */
        this.scaleJson = '';
        /**
         * The local matrix of this element.
         */
        this.matrix = bundle.matrix4.makeIdentity();
        /**
         * Disables occlusion testing for this element. Defaults to enabled. When
         * enabled, the elements position will be tested against the current depth
         * buffer. If the position is occluded, then the `occluded` attribute will be
         * set.
         */
        this.occlusionOff = false;
        /**
         * Indicates if the element is hidden by geometry. This property can be used
         * with a CSS selector to modify the appearance of the element when its
         * occluded.
         *
         * @example
         *
         * ```html
         * <style>
         *   vertex-viewer-dom-element[occluded] {
         *     opacity: 0;
         *   }
         * </style>
         * ```
         */
        this.occluded = false;
        /**
         * **EXPERIMENTAL**
         * Disables detached testing for this element. Defaults to disabled. When
         * enabled, the elements position will be tested against the current depth
         * buffer. If the position is detached, then the `detached` attribute will be
         * set.
         */
        this.detachedOff = true;
        /**
         * **EXPERIMENTAL**
         * Indicates if the element is detached from geometry. This property can be used
         * with a CSS selector to modify the appearance of the element when its
         * detached.
         *
         * @example
         *
         * ```html
         * <style>
         *   vertex-viewer-dom-element[detached] {
         *     opacity: 0;
         *   }
         * </style>
         * ```
         */
        this.detached = false;
        /**
         * Disables the billboarding behavior of the element. When billboarding is
         * enabled, the element will always be oriented towards the screen.
         */
        this.billboardOff = false;
        /**
         * Disables interaction events from children.
         */
        this.interactionsOff = false;
    }
    /**
     * @ignore
     */
    handlePositionChange() {
        this.syncMatrix();
    }
    /**
     * @ignore
     */
    handlePositionJsonChanged() {
        this.syncPosition();
    }
    /**
     * @ignore
     */
    handleRotationChanged() {
        this.syncQuaternionWithRotation();
    }
    /**
     * @ignore
     */
    handleRotationJsonChanged() {
        this.syncRotation();
    }
    /**
     * @ignore
     */
    handleQuaternionChange() {
        this.syncMatrix();
    }
    /**
     * @ignore
     */
    handleQuaternionJsonChanged() {
        this.syncQuaternion();
    }
    /**
     * @ignore
     */
    handleScaleChange() {
        this.syncMatrix();
    }
    /**
     * @ignore
     */
    handleScaleJsonChanged() {
        this.syncScale();
    }
    /**
     * @ignore
     */
    handleMatrixChanged(newMatrix, oldMatrix) {
        if (!bundle$1.objects.isEqual(newMatrix, oldMatrix)) {
            this.propertyChange.emit();
        }
    }
    /**
     * @ignore
     */
    connectedCallback() {
        this.syncProperties();
    }
    /**
     * @ignore
     */
    componentShouldUpdate(newValue, oldValue, prop) {
        return prop === 'occluded';
    }
    /**
     * @ignore
     */
    handleOcclusionStateChanged() {
        this.occlusionStateChanged.emit(this.occluded);
    }
    /**
     * @ignore
     */
    handleDetachedStateChanged() {
        this.detachedStateChanged.emit(this.detached);
    }
    syncProperties() {
        this.syncPosition();
        this.syncRotation();
        this.syncQuaternion();
        this.syncScale();
        this.syncMatrix();
    }
    syncPosition() {
        this.position =
            this.positionJson.length > 0
                ? this.parseJson('positionJson', this.positionJson, bundle.vector3.fromJson)
                : this.position;
    }
    syncRotation() {
        this.rotation =
            this.rotationJson != null && this.rotationJson.length > 0
                ? this.parseJson('rotationJson', this.rotationJson, bundle.euler.fromJson)
                : this.rotation;
        this.syncQuaternionWithRotation();
    }
    syncQuaternionWithRotation() {
        this.quaternion =
            this.rotation != null
                ? bundle.quaternion.fromEuler(this.rotation)
                : this.quaternion;
    }
    syncQuaternion() {
        this.quaternion =
            this.quaternionJson.length > 0
                ? this.parseJson('quaternionJson', this.quaternionJson, bundle.quaternion.fromJson)
                : this.quaternion;
    }
    syncScale() {
        this.scale =
            this.scaleJson.length > 0
                ? this.parseJson('scaleJson', this.scaleJson, bundle.vector3.fromJson)
                : this.scale;
    }
    syncMatrix() {
        this.matrix = bundle.matrix4.makeTRS(this.position, this.quaternion, this.scale);
    }
    parseJson(propName, value, parse) {
        try {
            return parse(value);
        }
        catch (e) {
            console.warn(`Could not parse \`${propName}\`. Invalid JSON.`);
            throw e;
        }
    }
    /**
     * @ignore
     */
    render() {
        return (index.h(index.Host, { key: '30ea00b8927b7e205c3b8929853acbfe75cd1fd8' }, index.h("slot", { key: 'd74b2da44968c21b7e8fb425b672cf270c1d3fad' })));
    }
    static get watchers() { return {
        "position": [{
                "handlePositionChange": 0
            }],
        "positionJson": [{
                "handlePositionJsonChanged": 0
            }],
        "rotation": [{
                "handleRotationChanged": 0
            }],
        "rotationJson": [{
                "handleRotationJsonChanged": 0
            }],
        "quaternion": [{
                "handleQuaternionChange": 0
            }],
        "quaternionJson": [{
                "handleQuaternionJsonChanged": 0
            }],
        "scale": [{
                "handleScaleChange": 0
            }],
        "scaleJson": [{
                "handleScaleJsonChanged": 0
            }],
        "matrix": [{
                "handleMatrixChanged": 0
            }],
        "occluded": [{
                "handleOcclusionStateChanged": 0
            }],
        "detached": [{
                "handleDetachedStateChanged": 0
            }]
    }; }
};
ViewerDomElement.style = viewerDomElementCss();

function isVertexViewerDomElement(el) {
    return (el instanceof HTMLElement &&
        el.nodeName === 'VERTEX-VIEWER-DOM-ELEMENT' &&
        el.matrix != null);
}

function isVertexViewerDomGroup(el) {
    return (((el instanceof HTMLElement && el.nodeName === 'VERTEX-VIEWER-DOM-GROUP') ||
        (el instanceof HTMLElement && el.dataset.isDomGroupElement != null)) &&
        el.matrix != null);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Renderer2d = (_, children) => {
    return index.h("div", { class: "root-2d" }, children);
};
function update2d(hostEl, parentWorldMatrix, viewport, camera, depthBuffer) {
    const elements = getElementDepths(hostEl, parentWorldMatrix, camera).sort((a, b) => a.distanceToCamera - b.distanceToCamera);
    for (let i = 0; i < elements.length; i++) {
        const { element, worldMatrix, worldPosition } = elements[i];
        const depthBufferIsNull = depthBuffer == null;
        const occluded = depthBufferIsNull ||
            (!element.occlusionOff &&
                (depthBuffer === null || depthBuffer === void 0 ? void 0 : depthBuffer.isOccluded(worldPosition, viewport)));
        const detached = depthBufferIsNull ||
            (!element.detachedOff &&
                (depthBuffer === null || depthBuffer === void 0 ? void 0 : depthBuffer.isDetached(worldPosition, viewport)));
        const screenPt = getScreenPosition(worldPosition, camera.projectionViewMatrix, viewport);
        updateTransform(element, screenPt);
        updateDepth(element, i, elements.length);
        update2d(element, worldMatrix, viewport, camera, depthBuffer);
        element.occluded = occluded !== null && occluded !== void 0 ? occluded : false;
        element.detached = detached !== null && detached !== void 0 ? detached : false;
        element.classList.add('ready');
    }
}
function getElementDepths(element, parentWorldMatrix, camera) {
    const results = [];
    for (let i = 0; i < element.children.length; i++) {
        const child = element.children[i];
        if (isVertexViewerDomGroup(child)) {
            const worldMatrix = bundle.matrix4.multiply(parentWorldMatrix, child.matrix);
            results.push(...getElementDepths(child, worldMatrix, camera));
        }
        else if (isVertexViewerDomElement(child)) {
            const worldMatrix = bundle.matrix4.multiply(parentWorldMatrix, child.matrix);
            const worldPosition = bundle.vector3.fromMatrixPosition(worldMatrix);
            const distanceToCamera = bundle.vector3.distanceSquared(camera.position, worldPosition);
            results.push({
                element: child,
                worldMatrix,
                worldPosition,
                distanceToCamera,
            });
        }
        else {
            results.push(...getElementDepths(child, parentWorldMatrix, camera));
        }
    }
    return results;
}
function updateTransform(element, relativePt) {
    element.style.transform = [
        `translate(-50%, -50%)`,
        `translate(${relativePt.x}px, ${relativePt.y}px)`,
    ].join(' ');
}
function updateDepth(element, index, elementCount) {
    element.style.zIndex = `${elementCount - index}`;
}
function getScreenPosition(pt, projectionViewMatrix, viewport) {
    const ndcPt = bundle.vector3.transformMatrix(pt, projectionViewMatrix);
    return viewport.transformVectorToViewport(ndcPt);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Renderer3d = ({ camera, viewport }, children) => {
    const pMatrix = bundle.matrix4.toObject(camera.projectionMatrix);
    const fovY = camera.isOrthographic()
        ? ((camera.far * 2) / (camera.fovHeight * 2)) * (viewport.height / 2)
        : pMatrix.m22 * (viewport.height / 2);
    const cameraTransform = [
        `translateZ(${fovY}px)`,
        getCameraCssMatrix(camera.viewMatrix),
        `translate(${viewport.width / 2}px, ${viewport.height / 2}px)`,
    ].join(' ');
    return (index.h("div", { class: "root-3d", style: { perspective: `${fovY}px` } }, index.h("div", { class: "camera", style: { transform: cameraTransform } }, children)));
};
function update3d(element, parentWorldMatrix, viewport, camera, depthBuffer) {
    for (let i = 0; i < element.children.length; i++) {
        const el = element.children[i];
        if (isVertexViewerDomElement(el)) {
            updateElement(el, parentWorldMatrix, viewport, camera, depthBuffer);
        }
        else if (isVertexViewerDomGroup(el)) {
            updateGroup(el, parentWorldMatrix, viewport, camera, depthBuffer);
        }
        else {
            update3d(el, parentWorldMatrix, viewport, camera, depthBuffer);
        }
    }
}
function updateElement(element, parentWorldMatrix, viewport, camera, depthBuffer) {
    const worldMatrix = bundle.matrix4.multiply(parentWorldMatrix, element.matrix);
    const positionWorld = bundle.vector3.fromMatrixPosition(worldMatrix);
    const depthBufferIsNull = depthBuffer == null;
    const occluded = depthBufferIsNull ||
        (!element.occlusionOff && (depthBuffer === null || depthBuffer === void 0 ? void 0 : depthBuffer.isOccluded(positionWorld, viewport)));
    element.occluded = occluded !== null && occluded !== void 0 ? occluded : false;
    const detached = depthBufferIsNull ||
        (!element.detachedOff && (depthBuffer === null || depthBuffer === void 0 ? void 0 : depthBuffer.isDetached(positionWorld, viewport)));
    element.detached = detached !== null && detached !== void 0 ? detached : false;
    element.classList.add('ready');
    if (element.billboardOff) {
        element.style.transform = getElementCssMatrix(worldMatrix);
    }
    else {
        let m = camera.viewMatrix;
        m = bundle.matrix4.transpose(m);
        m = bundle.matrix4.position(m, worldMatrix);
        m = bundle.matrix4.scale(m, element.scale);
        m[3] = 0;
        m[7] = 0;
        m[11] = 0;
        m[15] = 1;
        element.style.transform = getElementCssMatrix(m);
    }
    update3d(element, worldMatrix, viewport, camera, depthBuffer);
}
function updateGroup(element, parentWorldMatrix, viewport, camera, depthBuffer) {
    const worldMatrix = bundle.matrix4.multiply(parentWorldMatrix, element.matrix);
    update3d(element, worldMatrix, viewport, camera, depthBuffer);
}
function getCameraCssMatrix(viewMatrix) {
    const elements = [
        epsilon(viewMatrix[0]),
        epsilon(-viewMatrix[1]),
        epsilon(viewMatrix[2]),
        epsilon(viewMatrix[3]),
        epsilon(viewMatrix[4]),
        epsilon(-viewMatrix[5]),
        epsilon(viewMatrix[6]),
        epsilon(viewMatrix[7]),
        epsilon(viewMatrix[8]),
        epsilon(-viewMatrix[9]),
        epsilon(viewMatrix[10]),
        epsilon(viewMatrix[11]),
        epsilon(viewMatrix[12]),
        epsilon(-viewMatrix[13]),
        epsilon(viewMatrix[14]),
        epsilon(viewMatrix[15]),
    ].join(', ');
    return `matrix3d(${elements})`;
}
function getElementCssMatrix(matrix) {
    const values = [
        epsilon(matrix[0]),
        epsilon(matrix[1]),
        epsilon(matrix[2]),
        epsilon(matrix[3]),
        epsilon(-matrix[4]),
        epsilon(-matrix[5]),
        epsilon(-matrix[6]),
        epsilon(-matrix[7]),
        epsilon(matrix[8]),
        epsilon(matrix[9]),
        epsilon(matrix[10]),
        epsilon(matrix[11]),
        epsilon(matrix[12]),
        epsilon(matrix[13]),
        epsilon(matrix[14]),
        epsilon(matrix[15]),
    ].join(', ');
    return [`translate(-50%, -50%)`, `matrix3d(${values})`].join(' ');
}
function epsilon(value) {
    return Math.abs(value) < 1e-10 ? 0 : value;
}

const viewerDomRendererCss = () => `.sc-vertex-viewer-dom-renderer-h{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden;pointer-events:none}.root-3d.sc-vertex-viewer-dom-renderer{width:100%;height:100%;pointer-events:inherit}.camera.sc-vertex-viewer-dom-renderer{transform-style:preserve-3d;pointer-events:inherit;width:100%;height:100%}`;

const ViewerDomRenderer = class {
    constructor(hostRef) {
        index.registerInstance(this, hostRef);
        /**
         * Specifies the drawing mode for the renderer.
         *
         * When in `3d` mode, elements are positioned using CSS 3D transforms and will
         * scale and rotate with the camera. In `2d` mode, a simpler 2D transform is
         * used, and elements will not scale or rotate with camera changes.
         */
        this.drawMode = '3d';
        /**
         * Specifies whether to propagate events to the viewer's interaction handlers
         *
         * When `true` this <vertex-viewer-dom-renderer> will be registered as a valid event target
         * for the viewer. This enables camera interactions to be initiated from elements within this renderer.
         *
         * When `false` this <vertex-viewer-dom-renderer> will *not* be registered as a target
         * for the viewer.
         */
        this.propagateEventsToViewer = true;
        this.viewport = new viewport.Viewport(0, 0);
        this.invalidateFrameCounter = 0;
        this.interactionDisposables = [];
        this.handleViewerFrameDrawn = async () => {
            this.updatePropsFromViewer();
        };
    }
    /**
     * @ignore
     */
    componentWillLoad() {
        var _a;
        const resized = new ResizeObserver(() => this.handleResize());
        resized.observe(this.hostEl);
        const mutation = new MutationObserver(() => this.handleChildrenChange());
        mutation.observe(this.hostEl, { childList: true });
        this.handleViewerChange(this.viewer, undefined);
        if (((_a = this.viewer) === null || _a === void 0 ? void 0 : _a.frame) != null) {
            this.handleViewerFrameDrawn();
        }
    }
    /**
     * @ignore
     */
    render() {
        if (this.camera != null) {
            if (this.drawMode === '2d') {
                return (index.h(index.Host, null, index.h(Renderer2d, null, index.h("slot", null))));
            }
            else {
                return (index.h(index.Host, null, index.h(Renderer3d, { camera: this.camera, viewport: this.viewport }, index.h("slot", null))));
            }
        }
        else {
            return index.h(index.Host, null);
        }
    }
    /**
     * @ignore
     */
    componentWillRender() {
        this.updateElements();
    }
    /**
     * @ignore
     */
    handleViewerChange(newViewer, oldViewer) {
        oldViewer === null || oldViewer === void 0 ? void 0 : oldViewer.removeEventListener('frameDrawn', this.handleViewerFrameDrawn);
        newViewer === null || newViewer === void 0 ? void 0 : newViewer.addEventListener('frameDrawn', this.handleViewerFrameDrawn);
        if (this.propagateEventsToViewer && newViewer != null) {
            this.handleEventPropagationToViewer(newViewer);
        }
    }
    /**
     * @ignore
     */
    handlePropertyChange() {
        this.invalidateFrame();
    }
    invalidateFrame() {
        this.invalidateFrameCounter = this.invalidateFrameCounter + 1;
    }
    /**
     * disposes any existing disposables, and registers new handlers on the newly provided viewer.
     * @param newViewer
     */
    handleEventPropagationToViewer(newViewer) {
        this.interactionDisposables.forEach((disposable) => {
            disposable.dispose();
        });
        this.interactionDisposables = [];
        newViewer.getInteractionHandlers().then((handlers) => {
            handlers.forEach((handler) => {
                if (handler instanceof multiElementInteractionHandler.MultiElementInteractionHandler) {
                    const disposable = handler.registerAdditionalElement(this.hostEl);
                    this.interactionDisposables = [
                        ...this.interactionDisposables,
                        disposable,
                    ];
                }
            });
        });
    }
    async updateElements() {
        const { viewport, camera } = this;
        const worldMatrix = bundle.matrix4.makeIdentity();
        if (camera != null) {
            if (this.drawMode === '3d') {
                update3d(this.hostEl, worldMatrix, viewport, camera, this.depthBuffer);
            }
            else {
                update2d(this.hostEl, worldMatrix, viewport, camera, this.depthBuffer);
            }
        }
    }
    handleResize() {
        const bounds = this.hostEl.getBoundingClientRect();
        this.viewport = new viewport.Viewport(bounds.width, bounds.height);
    }
    handleChildrenChange() {
        this.invalidateFrame();
    }
    async updatePropsFromViewer() {
        const { frame } = this.viewer || {};
        this.depthBuffer = await (frame === null || frame === void 0 ? void 0 : frame.depthBuffer());
        this.camera = frame === null || frame === void 0 ? void 0 : frame.scene.camera;
    }
    get hostEl() { return index.getElement(this); }
    static get watchers() { return {
        "viewer": [{
                "handleViewerChange": 0
            }]
    }; }
};
ViewerDomRenderer.style = viewerDomRendererCss();

exports.vertex_viewer_dom_element = ViewerDomElement;
exports.vertex_viewer_dom_renderer = ViewerDomRenderer;
//# sourceMappingURL=vertex-viewer-dom-element.vertex-viewer-dom-renderer.entry.cjs.js.map

//# sourceMappingURL=vertex-viewer-dom-element_2.cjs.entry.js.map