ObjectsSelect.js 9.81 KB
import { EventDispatcher, Vector2, Mesh, Raycaster } from 'three';
import { acceleratedRaycast } from 'three-mesh-bvh';
import { getMousePosition } from '@/utility/jsm/function/getMousePosition';
import {
    MODEL_COLOR,
    SLECTED_MODEL_COLOR,
    BOUNDARYBOX_COLOR,
    getSceneMeshMaterial
} from '@/utility/constants/materials';

import BoxHelper from '@/utility/jsm/draw/BoxHelper';

Mesh.prototype.raycast = acceleratedRaycast;

class ObjectsSelect extends EventDispatcher {
    constructor(editor, rootObjects = []) {
        super();
        const { camera, scene, domElement, control } = editor;

        /* eslint no-underscore-dangle: 0 */
        this._camera = camera;
        this._scene = scene;
        this._domElement = domElement;
        this._mouseControl = control;

        //
        this.selectedBoxMeshs = [];
        this.selectedMeshs = [];
        this.rootObjects = rootObjects;

        this.raycaster = new Raycaster();

        this.addEvents();

        this.enabled = true;
        this.ismoving = false;

        this.keyPressCtrl = false;
        this.isBoundingBoxVisible = true;

        this.counts = 0;
    }

    dispose = () => {
        this.removeEvents();
    };

    addEvents = () => {
        this._domElement.addEventListener('pointerdown', this.onPointerdown);
        this._domElement.addEventListener('pointerup', this.onPointerup);
        this._domElement.addEventListener('pointermove', this.onPointermove);
        // document.addEventListener('keydown', this.onKeydown);
        // document.addEventListener('keyup', this.onKeyup);
    };

    removeEvents = () => {
        this._domElement.removeEventListener('pointerdown', this.onPointerdown);
        this._domElement.removeEventListener('pointerup', this.onPointerup);
        this._domElement.removeEventListener('pointermove', this.onPointermove);
    };
    /*
    onKeydown = (e) => {
        this.keyPressCtrl = e.key === 'Control';
    };

    onKeyup = (e) => {
        this.keyPressCtrl = false;
    };
*/

    onPointerdown = (e) => {
        if (!this.enabled) return;
        if (!this._camera) return;
        if (!this._domElement) return;
        if (e.button !== 0) return;
        this.ismoving = false;
    };

    onPointermove = (e) => {
        if (!this.enabled) return;
        if (!this._camera) return;
        if (!this._domElement) return;

        const vec2d = getMousePosition(e, this._domElement);
        const mouse = new Vector2(vec2d.x, vec2d.y);

        this.raycaster.firstHitOnly = true;
        this._camera.near = 1;
        this.raycaster.setFromCamera(mouse, this._camera);

        const objects = this._scene.children.filter(
            (el) => el.name.includes('active:') && el.isMesh === true && el.visible
        );
        const detects = this.rootObjects.length === 0 ? objects : this.rootObjects;
        const res = this.raycaster.intersectObjects(detects);

        if (res.length > 0) {
            if (this.hoverMoveMesh !== res[0].object.name) {
                this.hoverMoveMesh = res[0].object;
                // this.hoverMoveMesh.material = getSceneMeshMaterial(this.hoverMoveMesh.material.color);
            }
        } else {
            this.hoverMoveMesh = null;
        }

        this._camera.near = -10000;
    };

    onPointerup = (e) => {
        if (!this.enabled) return;
        if (e.button !== 0) return;
        if (this.ismoving) return;
        if (this.hoverMoveMesh) {
            const meshContain = this.selectedMeshs.filter((el) => el.name === this.hoverMoveMesh.name);
            let meshs = [];
            if (this.keyPressCtrl) {
                meshs =
                    meshContain.length > 0
                        ? this.selectedMeshs.filter((el) => el.name !== this.hoverMoveMesh.name)
                        : [...this.selectedMeshs, this.hoverMoveMesh];
            } else {
                meshs = [this.hoverMoveMesh];
            }
            this.pickUpMeshs(meshs);
        } else {
            if (this.keyPressCtrl) return;
            this.setUnSelected();
            this.selectedMeshs = [];
        }
        this.sendEventToSetMeshInfo();
        this.dispatchEvent({
            type: 'end',
            message: {
                isSelected: this.selectedMeshs.length > 0
            }
        });
    };

    pickUpMeshs = (objects) => {
        this.selectedMeshs = objects;
        this.setUnSelected();
        this.selectedMeshs.forEach((el) => {
            el.renderOrder = 1;
            if (el.geometry.groups.length > 0 && el.meshType === 'stx') {
                el.material[0].color.setHex(SLECTED_MODEL_COLOR);
            } else {
                el.material.color.setHex(SLECTED_MODEL_COLOR);
            }
            el.selected = true;
            if (!el.BoxHelper) {
                const helper = new BoxHelper(el, BOUNDARYBOX_COLOR);
                helper.name = `${el.name}:boxhelper`;
                this._scene.add(helper);
                el.BoxHelper = helper;
            } else {
                el.BoxHelper.visible = true;
            }
            this.updateBoundingBox(el);
        });
    };

    setSelectedMeshs = (objects) => {
        this.selectedMeshs = objects;
    };

    pickUpAllMeshs = () => {
        const objects = this._scene.children.filter((el) => el.name.includes('active:') && el.isMesh);
        if (objects) {
            this.pickUpMeshs(objects);
        }
        this.sendEventToSetMeshInfo();
    };

    setUnSelected = () => {
        if (!this._scene) return;
        const objects = this._scene.children.filter((el) => el.name.includes('active:') && el.isMesh);
        if (objects) {
            objects.forEach((el) => {
                el.renderOrder = 0;
                if (el.geometry.groups.length > 0 && el.meshType === 'stx') {
                    el.material[0].color.setHex(MODEL_COLOR);
                } else {
                    el.material.color.setHex(MODEL_COLOR);
                }
                el.selected = false;
                if (el.BoxHelper) el.BoxHelper.visible = false;
            });
        }
    };

    updateControls = (object) => {
        this.updateBoundingBox(object);
    };

    updateBoundingBox = (object) => {
        if (!object) return;
        if (!object.BoxHelper) return;
        object.BoxHelper.setFromObject(object, this.updateSlicingRange);
        object.BoxHelper.visible = this.isBoundingBoxVisible;
    };

    updateBoundingBoxByOffset = (object, offset) => {
        if (!object) return;
        if (!object.BoxHelper) return;
        object.BoxHelper.setFromObjectByOffset(object, offset, this.updateSlicingRange);
        object.BoxHelper.visible = this.isBoundingBoxVisible;
    };

    updateSlicingRange = () => {
        this.counts++;
        if (this.counts === this.selectedMeshs.length) {
            this.dispatchEvent({
                type: 'update-slicingRange'
            });
        }
        // this.sendEventToSetMeshInfo();
    };

    setObjectsControlIsMoving = (val, offset) => {
        this.counts = 0;
        this.ismoving = val;
        if (this.ismoving) return;
        this.selectedMeshs.forEach((el) => {
            if (offset) {
                this.updateBoundingBoxByOffset(el, offset);
            } else {
                this.updateBoundingBox(el);
            }
        });
    };

    setBoundingBoxVisible = (val) => {
        this.isBoundingBoxVisible = val;
        this.setBoxHelperVisible(val);
    };

    setBoxHelperVisible = (val) => {
        const objects = this._scene.children.filter((el) => el.name.includes('active:') && el.isMesh === true);
        if (objects) {
            objects.forEach((el) => {
                if (el.BoxHelper) el.BoxHelper.visible = val;
            });
        }
    };

    getMeshInformations = () => {
        let volume = 0;
        let triangles = 0;
        let length = 0;
        let width = 0;
        let height = 0;
        let selectedModelListKeys = [];
        if (this.selectedMeshs.length === 1) {
            const mesh = this.selectedMeshs[0];
            const { max, min } = mesh.box;
            const vector = max.clone().sub(min);
            volume = mesh.geometry.volume;
            triangles = mesh.geometry.index.count;
            length = vector.x;
            width = vector.y;
            height = vector.z;
            selectedModelListKeys = [mesh.name];
        }
        if (this.selectedMeshs.length > 1) {
            let xmax = -Infinity;
            let ymax = -Infinity;
            let zmax = -Infinity;
            let xmin = Infinity;
            let ymin = Infinity;
            let zmin = Infinity;
            this.selectedMeshs.forEach((el) => {
                const { max, min } = el.box;
                volume += el.geometry.volume;
                triangles += el.geometry.index.count;

                xmax = Math.max(xmax, max.x);
                ymax = Math.max(ymax, max.y);
                zmax = Math.max(zmax, max.z);
                xmin = Math.min(xmin, min.x);
                ymin = Math.min(ymin, min.y);
                zmin = Math.min(zmin, min.z);

                selectedModelListKeys = [...selectedModelListKeys, el.name];
            });
            length = xmax - xmin;
            width = ymax - ymin;
            height = zmax - zmin;
        }
        return {
            volume,
            triangles,
            length,
            width,
            height,
            selectedModelListKeys
        };
    };

    sendEventToSetMeshInfo = () => {
        const { volume, triangles, length, width, height, selectedModelListKeys } = this.getMeshInformations();
        this.dispatchEvent({
            type: 'selected',
            message: {
                volume,
                triangles,
                length,
                width,
                height,
                selectedModelListKeys,
                selectedMeshs: this.selectedMeshs
            }
        });
    };
}
export default ObjectsSelect;