Reference Source

src/viewer/scene/CameraControl/lib/controllers/PickController.js

import {math} from "../../../math/math.js";
import {Scene} from "../../../scene/Scene.js";
import {PickResult} from "../../../webgl/PickResult.js";

const DEFAULT_SNAP_PICK_RADIUS = 45;
const DEFAULT_SNAP_MODE = "vertex";

/**
 *
 * @private
 */
class PickController {

    constructor(cameraControl, configs) {
        /**
         * @type {Scene}
         */
        this._scene = cameraControl.scene;

        this._cameraControl = cameraControl;

        this._scene.canvas.canvas.oncontextmenu = function (e) {
            e.preventDefault();
        };

        this._configs = configs;

        /**
         * Set true to schedule picking of an Entity.
         * @type {boolean}
         */
        this.schedulePickEntity = false;

        /**
         * Set true to schedule picking of a position on teh surface of an Entity.
         * @type {boolean}
         */
        this.schedulePickSurface = false;

        /**
         * Set true to schedule snap-picking with surface picking as a fallback - used for measurement.
         * @type {boolean}
         */
        this.scheduleSnapOrPick = false;

        /**
         * The canvas position at which to do the next scheduled pick.
         * @type {Number[]}
         */
        this.pickCursorPos = math.vec2();

        /**
         * Will be true after picking to indicate that something was picked.
         * @type {boolean}
         */
        this.picked = false;

        /**
         * Will be true after picking to indicate that a position on the surface of an Entity was picked.
         * @type {boolean}
         */
        this.pickedSurface = false;

        /**
         * Will hold the PickResult after after picking.
         * @type {PickResult}
         */
        this.pickResult = null;

        this._lastPickedEntityId = null;

        this._lastHash = null;

        this._needFireEvents = 0;
    }

    /**
     * Immediately attempts a pick, if scheduled.
     */
    update() {

        if (!this._configs.pointerEnabled) {
            return;
        }

        if (!this.schedulePickEntity && !this.schedulePickSurface) {
            return;
        }

        const hash = `${~~this.pickCursorPos[0]}-${~~this.pickCursorPos[1]}-${this.scheduleSnapOrPick}-${this.schedulePickSurface}-${this.schedulePickEntity}`;
        if (this._lastHash === hash) {
            return;
        }

        this.picked = false;
        this.pickedSurface = false;
        this.snappedOrPicked = false;
        this.hoveredSnappedOrSurfaceOff = false;

        const hasHoverSurfaceSubs = this._cameraControl.hasSubs("hoverSurface");

        if (this.scheduleSnapOrPick) {
            const snapPickResult = this._scene.pick({
                canvasPos: this.pickCursorPos,
                snapRadius: this._configs.snapRadius,
                snapToVertex: this._configs.snapToVertex,
                snapToEdge: this._configs.snapToEdge,
            });
            if (snapPickResult && (snapPickResult.snappedToEdge || snapPickResult.snappedToVertex)) {
                this.snapPickResult = snapPickResult;
                this.snappedOrPicked = true;
                this._needFireEvents++;
            } else {
                this.schedulePickSurface = true; // Fallback
                this.snapPickResult = null;
            }
        }

        if (this.schedulePickSurface) {
            if (this.pickResult && this.pickResult.worldPos) {
                const pickResultCanvasPos = this.pickResult.canvasPos;
                if (pickResultCanvasPos[0] === this.pickCursorPos[0] && pickResultCanvasPos[1] === this.pickCursorPos[1]) {
                    this.picked = true;
                    this.pickedSurface = true;
                    this._needFireEvents += hasHoverSurfaceSubs ? 1 : 0;
                    this.schedulePickEntity = false;
                    this.schedulePickSurface = false;
                    if (this.scheduleSnapOrPick) {
                        this.snappedOrPicked = true;
                    } else {
                        this.hoveredSnappedOrSurfaceOff = true;
                    }
                    this.scheduleSnapOrPick = false;
                    return;
                }
            }
        }

        if (this.schedulePickEntity) {
            if (this.pickResult && (this.pickResult.canvasPos || this.pickResult.snappedCanvasPos)) {
                const pickResultCanvasPos = this.pickResult.canvasPos || this.pickResult.snappedCanvasPos;
                if (pickResultCanvasPos[0] === this.pickCursorPos[0] && pickResultCanvasPos[1] === this.pickCursorPos[1]) {
                    this.picked = true;
                    this.pickedSurface = false;
                    this.schedulePickEntity = false;
                    this.schedulePickSurface = false;
                    return;
                }
            }
        }

        if (this.schedulePickSurface || (this.scheduleSnapOrPick && !this.snapPickResult)) {
            this.pickResult = this._scene.pick({
                pickSurface: true,
                pickSurfaceNormal: false,
                canvasPos: this.pickCursorPos
            });
            if (this.pickResult) {
                this.picked = true;
                if (this.scheduleSnapOrPick) {
                    this.snappedOrPicked = true;
                } else {
                    this.pickedSurface = true;
                }
                this._needFireEvents++;
            } else if (this.scheduleSnapOrPick) {
                this.hoveredSnappedOrSurfaceOff = true;
                this._needFireEvents++;
            }

        } else { // schedulePickEntity == true

            this.pickResult = this._scene.pick({
                canvasPos: this.pickCursorPos
            });

            if (this.pickResult) {
                this.picked = true;
                this.pickedSurface = false;
                this._needFireEvents++;
            }
        }

        this.scheduleSnapOrPick = false;
        this.schedulePickEntity = false;
        this.schedulePickSurface = false;
    }

    fireEvents() {

        if (this._needFireEvents === 0) {
            return;
        }

        if (this.hoveredSnappedOrSurfaceOff) {
            this._cameraControl.fire("hoverSnapOrSurfaceOff", {
                canvasPos: this.pickCursorPos,
                pointerPos : this.pickCursorPos
            }, true);
        }

        if (this.snappedOrPicked) {
            if (this.snapPickResult) {
                const pickResult = new PickResult();
                pickResult.entity = this.snapPickResult.entity;
                pickResult.snappedToVertex = this.snapPickResult.snappedToVertex;
                pickResult.snappedToEdge = this.snapPickResult.snappedToEdge;
                pickResult.worldPos = this.snapPickResult.worldPos;
                pickResult.canvasPos = this.pickCursorPos
                pickResult.snappedCanvasPos = this.snapPickResult.snappedCanvasPos;
                this._cameraControl.fire("hoverSnapOrSurface", pickResult, true);
                this.snapPickResult = null;
            } else {
                this._cameraControl.fire("hoverSnapOrSurface", this.pickResult, true);
            }
        } else {

        }

        if (this.picked && this.pickResult && (this.pickResult.entity || this.pickResult.worldPos)) {

            if (this.pickResult.entity) {

                const pickedEntityId = this.pickResult.entity.id;

                if (this._lastPickedEntityId !== pickedEntityId) {

                    if (this._lastPickedEntityId !== undefined) {
                        this._cameraControl.fire("hoverOut", {
                            entity: this._scene.objects[this._lastPickedEntityId]
                        }, true);
                    }

                    this._cameraControl.fire("hoverEnter", this.pickResult, true);
                    this._lastPickedEntityId = pickedEntityId;
                }
            }

            this._cameraControl.fire("hover", this.pickResult, true);

            if (this.pickResult.worldPos) {
                this.pickedSurface = true;
                this._cameraControl.fire("hoverSurface", this.pickResult, true);
            }

        } else {

            if (this._lastPickedEntityId !== undefined) {
                this._cameraControl.fire("hoverOut", {
                    entity: this._scene.objects[this._lastPickedEntityId]
                }, true);
                this._lastPickedEntityId = undefined;
            }

            this._cameraControl.fire("hoverOff", {
                canvasPos: this.pickCursorPos
            }, true);
        }

        this.pickResult = null;

        this._needFireEvents = 0;
    }
}

export {PickController};