/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
import { r as registerInstance, c as createEvent, h, H as Host } from './index-EpnuuCxl.js';
import { p as point, v as vector3, c as boundingBox, b as plane, r as ray } from './bundle-DfdVaPrt.js';
import { g as getMouseClientPosition } from './dom-D6my8OyX.js';
import { E as ElementRectObserver } from './elementRectObserver-CFus3r7d.js';
import { W as WalkModeModel, a as WalkModeController } from './model-Dru_A3Gf.js';
import './bundle-DLfw87TV.js';
import './_commonjsHelpers-CG9AChtV.js';

class TeleportInteractionHandler {
    constructor(model, animationConfiguration) {
        this.model = model;
        this.animationConfiguration = animationConfiguration;
        this.rectObserver = new ElementRectObserver();
        this.handlePointerDown = this.handlePointerDown.bind(this);
        this.handlePointerUp = this.handlePointerUp.bind(this);
        this.handleEnabledChange = this.handleEnabledChange.bind(this);
        this.handleTeleportModeChange = this.handleTeleportModeChange.bind(this);
        this.enabledChangeDisposable = this.model.onEnabledChange(this.handleEnabledChange);
    }
    dispose() {
        var _a;
        this.disable();
        (_a = this.enabledChangeDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        this.element = undefined;
        this.api = undefined;
        this.rectObserver.disconnect();
    }
    initialize(element, api) {
        this.element = element;
        this.api = api;
        this.rectObserver.observe(element);
        this.handleEnabledChange(this.model.getEnabled());
    }
    setAnimations(config) {
        this.animationConfiguration = config;
    }
    enable() {
        var _a;
        this.disable();
        this.teleportModeChangeDisposable = this.model.onTeleportModeChange(this.handleTeleportModeChange);
        (_a = this.element) === null || _a === void 0 ? void 0 : _a.addEventListener('pointerdown', this.handlePointerDown);
        this.handleTeleportModeChange(this.model.getTeleportMode());
    }
    disable() {
        var _a, _b, _c;
        (_a = this.teleportModeChangeDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        (_b = this.cursorDisposable) === null || _b === void 0 ? void 0 : _b.dispose();
        (_c = this.element) === null || _c === void 0 ? void 0 : _c.removeEventListener('pointerdown', this.handlePointerDown);
    }
    async handlePointerDown(event) {
        this.downPosition = getMouseClientPosition(event, this.rectObserver.rect);
        this.downButtons = event.buttons;
        window.addEventListener('pointerup', this.handlePointerUp);
    }
    async handlePointerUp(event) {
        var _a, _b;
        const mode = this.model.getTeleportMode();
        const threshold = (_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.pixelThreshold()) !== null && _b !== void 0 ? _b : 2;
        const point$1 = getMouseClientPosition(event, this.rectObserver.rect);
        const isRightClick = this.downButtons === 2;
        const hasModifier = event.shiftKey || event.altKey || event.metaKey || event.ctrlKey;
        if (mode != null &&
            this.downPosition != null &&
            point.distance(this.downPosition, point$1) <= threshold &&
            !isRightClick &&
            !hasModifier) {
            switch (mode) {
                case 'teleport':
                    return this.teleportToHit(point$1);
                case 'teleport-toward':
                    return this.teleportTowardHit(point$1);
                case 'teleport-and-align':
                    return this.teleportAndAlign(point$1);
            }
        }
        this.downPosition = undefined;
        window.removeEventListener('pointerup', this.handlePointerUp);
    }
    async beginInteraction() {
        var _a, _b;
        if (!((_a = this.api) === null || _a === void 0 ? void 0 : _a.isInteracting())) {
            await ((_b = this.api) === null || _b === void 0 ? void 0 : _b.beginInteraction());
        }
    }
    async endInteraction() {
        var _a;
        await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.endInteraction());
    }
    async teleportTowardHit(point) {
        var _a, _b;
        const worldPoint = await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.getWorldPointFromViewport(point));
        const mouseRay = (_b = this.api) === null || _b === void 0 ? void 0 : _b.getRayFromPoint(point);
        if (mouseRay != null) {
            const { teleportDistancePercentage, teleportCollisionDistance } = this.model.getConfiguration();
            const teleportDistanceScalar = teleportDistancePercentage / 100;
            return this.performInteraction(({ camera, boundingBox: boundingBox$1 }) => {
                const stepDistance = Math.max(...vector3.toArray(boundingBox.lengths(boundingBox$1))) *
                    teleportDistanceScalar;
                const positionPlane = plane.fromNormalAndCoplanarPoint(camera.up, camera.position);
                const projectedWorldPoint = plane.projectPoint(positionPlane, worldPoint !== null && worldPoint !== void 0 ? worldPoint : camera.position);
                const distanceToWorldPoint = vector3.distance(camera.position, projectedWorldPoint);
                if (distanceToWorldPoint < stepDistance &&
                    distanceToWorldPoint > teleportCollisionDistance * 1.1) {
                    const mouseRayAtWorldPoint = ray.create({
                        ...mouseRay,
                        origin: projectedWorldPoint,
                    });
                    const nextPosition = plane.projectPoint(positionPlane, ray.at(mouseRayAtWorldPoint, -teleportCollisionDistance));
                    return camera.update({
                        position: nextPosition,
                        lookAt: vector3.add(camera.lookAt, vector3.subtract(nextPosition, camera.position)),
                    });
                }
                else {
                    const projectedNextStep = plane.projectPoint(positionPlane, ray.at(mouseRay, stepDistance));
                    return camera.update({
                        position: projectedNextStep,
                        lookAt: vector3.add(camera.lookAt, vector3.subtract(projectedNextStep, camera.position)),
                    });
                }
            });
        }
    }
    async teleportToHit(point) {
        const worldPoint = await this.getWorldPointWithFallback(point);
        if (worldPoint != null) {
            return this.performInteraction(({ camera, boundingBox }) => {
                const shortestBoundingBoxLength = this.shortestLength(boundingBox);
                const heightScalar = this.model.getTeleportHeightPercentage() / 100;
                const cameraPlane = plane.fromNormalAndCoplanarPoint(camera.up, camera.position);
                const projectedHitPosition = plane.projectPoint(cameraPlane, worldPoint);
                const rayToHitPosition = ray.create({
                    origin: camera.position,
                    direction: vector3.normalize(vector3.subtract(projectedHitPosition, camera.position)),
                });
                const distanceToHitPosition = vector3.distance(camera.position, projectedHitPosition);
                const distanceToLookAt = vector3.distance(camera.position, camera.lookAt);
                const newPosition = ray.at(rayToHitPosition, distanceToHitPosition - shortestBoundingBoxLength * heightScalar);
                const newPositionViewVectorRay = ray.create({
                    origin: newPosition,
                    direction: vector3.normalize(camera.viewVector),
                });
                return camera.update({
                    position: newPosition,
                    lookAt: ray.at(newPositionViewVectorRay, distanceToLookAt),
                });
            });
        }
    }
    async teleportAndAlign(point) {
        var _a;
        const hits = await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.hitItems(point));
        const hit = hits != null ? hits[0] : undefined;
        if ((hit === null || hit === void 0 ? void 0 : hit.hitNormal) != null && (hit === null || hit === void 0 ? void 0 : hit.hitPoint)) {
            await this.performInteraction(({ camera, boundingBox }) => {
                const shortestBoundingBoxLength = this.shortestLength(boundingBox);
                const heightScalar = this.model.getTeleportHeightPercentage() / 100;
                const upRay = ray.create({
                    origin: hit.hitPoint,
                    direction: hit.hitNormal,
                });
                return camera.alignTo(ray.at(upRay, shortestBoundingBoxLength * heightScalar), hit.hitNormal);
            });
        }
    }
    async performInteraction(fn) {
        var _a;
        await this.beginInteraction();
        await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.transformCamera(fn, this.renderConfiguration()));
        await this.endInteraction();
    }
    async getWorldPointWithFallback(point) {
        var _a, _b;
        const worldPoint = await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.getWorldPointFromViewport(point));
        if (worldPoint == null) {
            const hits = await ((_b = this.api) === null || _b === void 0 ? void 0 : _b.hitItems(point));
            const hit = hits != null ? hits[0] : undefined;
            return (hit === null || hit === void 0 ? void 0 : hit.hitPoint) != null
                ? hit.hitPoint
                : worldPoint;
        }
        return worldPoint;
    }
    handleEnabledChange(enabled) {
        if (enabled) {
            this.enable();
        }
        else {
            this.disable();
        }
    }
    handleTeleportModeChange(mode) {
        var _a, _b;
        (_a = this.cursorDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        if (mode != null) {
            this.cursorDisposable = (_b = this.api) === null || _b === void 0 ? void 0 : _b.addCursor('crosshair');
        }
    }
    shortestLength(boundingBox$1) {
        return Math.min(...vector3.toArray(boundingBox.lengths(boundingBox$1)));
    }
    renderConfiguration() {
        return this.animationConfiguration != null
            ? { animation: { milliseconds: this.animationConfiguration.durationMs } }
            : undefined;
    }
}

const viewerTeleportToolCss = () => `:host{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none}`;

const ViewerTeleportTool = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.controllerChanged = createEvent(this, "controllerChanged");
        /**
         * Indicates whether animations will be used when performing camera
         * operations. Defaults to `false`.
         */
        this.animationsDisabled = false;
        /**
         * The duration of animations, in milliseconds. Defaults to `500`.
         */
        this.animationMs = 500;
        this.model = new WalkModeModel();
        this.stateMap = {};
    }
    /**
     * @ignore
     */
    componentWillLoad() {
        this.setupController();
        this.setupInteractionHandler();
        this.setDepthBuffers();
    }
    /**
     * @ignore
     */
    connectedCallback() {
        this.setupInteractionHandler();
        this.setDepthBuffers();
    }
    /**
     * @ignore
     */
    disconnectedCallback() {
        this.clearInteractionHandler();
        this.resetDepthBuffers();
    }
    /**
     * @ignore
     */
    handleModeChange() {
        var _a;
        (_a = this.controller) === null || _a === void 0 ? void 0 : _a.setTeleportMode(this.mode);
        if (this.mode != null) {
            this.setDepthBuffers();
        }
        else {
            this.resetDepthBuffers();
        }
    }
    /**
     * @ignore
     */
    handleViewerChanged() {
        this.clearInteractionHandler();
        this.setupInteractionHandler();
        this.setDepthBuffers();
    }
    /**
     * @ignore
     */
    handleAnimationMsChanged() {
        var _a;
        if (this.animationMs != null) {
            (_a = this.interactionHandler) === null || _a === void 0 ? void 0 : _a.setAnimations({ durationMs: this.animationMs });
        }
    }
    /**
     * @ignore
     */
    handleAnimationsDisabledChanged() {
        var _a, _b;
        if (this.animationsDisabled) {
            (_a = this.interactionHandler) === null || _a === void 0 ? void 0 : _a.setAnimations(undefined);
        }
        else if (this.animationMs != null) {
            (_b = this.interactionHandler) === null || _b === void 0 ? void 0 : _b.setAnimations({ durationMs: this.animationMs });
        }
    }
    /**
     * @ignore
     */
    handleControllerChanged() {
        this.setupInteractionHandler();
        this.setupController();
        this.controllerChanged.emit(this.controller);
    }
    handleModelChanged() {
        this.setupController();
        this.clearInteractionHandler();
        this.setupInteractionHandler();
    }
    render() {
        return h(Host, { key: 'c8a6512176d203d38ae647ccddcfa8adf307f977' });
    }
    setupController() {
        if (this.controller == null) {
            this.controller = new WalkModeController(this.model);
            this.controller.setTeleportMode(this.mode);
            this.controllerChanged.emit(this.controller);
        }
        else {
            this.controller.setTeleportMode(this.mode);
            this.controller.updateModel(this.model);
        }
    }
    setDepthBuffers() {
        if (this.mode != null &&
            this.viewer != null &&
            this.viewer.depthBuffers == null) {
            this.stateMap.shouldClearDepthBuffers = true;
            this.viewer.depthBuffers = 'final';
        }
    }
    resetDepthBuffers() {
        if (this.stateMap.shouldClearDepthBuffers && this.viewer != null) {
            this.viewer.depthBuffers = undefined;
            this.stateMap.shouldClearDepthBuffers = undefined;
        }
    }
    clearInteractionHandler() {
        var _a, _b;
        (_a = this.interactionHandlerDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        this.interactionHandlerDisposable = undefined;
        (_b = this.interactionHandler) === null || _b === void 0 ? void 0 : _b.dispose();
        this.interactionHandler = undefined;
    }
    async setupInteractionHandler() {
        var _a;
        if (this.interactionHandler == null) {
            this.interactionHandler = new TeleportInteractionHandler(this.model, !this.animationsDisabled && this.animationMs != null
                ? { durationMs: this.animationMs }
                : undefined);
            this.interactionHandlerDisposable =
                await ((_a = this.viewer) === null || _a === void 0 ? void 0 : _a.registerInteractionHandler(this.interactionHandler));
        }
    }
    static get watchers() { return {
        "mode": [{
                "handleModeChange": 0
            }],
        "viewer": [{
                "handleViewerChanged": 0
            }],
        "animationMs": [{
                "handleAnimationMsChanged": 0
            }],
        "animationsDisabled": [{
                "handleAnimationsDisabledChanged": 0
            }],
        "controller": [{
                "handleControllerChanged": 0
            }],
        "model": [{
                "handleModelChanged": 0
            }]
    }; }
};
ViewerTeleportTool.style = viewerTeleportToolCss();

export { ViewerTeleportTool as vertex_viewer_teleport_tool };
//# sourceMappingURL=vertex-viewer-teleport-tool.entry.js.map

//# sourceMappingURL=vertex-viewer-teleport-tool.entry.js.map