BoxHelper.js 9.08 KB
import { Vector3, BufferGeometry, LineBasicMaterial, BufferAttribute, LineSegments, Color, Object3D } from 'three';
import { workerGetMaxMinZValue } from '@/utility/jsm/function/getBoundary';
import { drawTextSprite, createTexture } from '@/utility/jsm/draw/drawTextSprite';

const getTextureMaterial = () => {
    const backColor = new Color(0xffffff);

    const xtColor = new Color(0x000000);
    const ytColor = new Color(0x000000);
    const ztColor = new Color(0x000000);

    const fontsize = 60;
    const backgroundColor = { r: backColor.r * 255, g: backColor.g * 255, b: backColor.b * 255, a: 0.0 };

    const xColor = { r: xtColor.r * 255, g: xtColor.g * 255, b: xtColor.b * 255, a: 1.0 };
    const yColor = { r: ytColor.r * 255, g: ytColor.g * 255, b: ytColor.b * 255, a: 1.0 };
    const zColor = { r: ztColor.r * 255, g: ztColor.g * 255, b: ztColor.b * 255, a: 1.0 };

    const xMaterial = {
        fontsize,
        backgroundColor,
        textColor: xColor
    };
    const yMaterial = {
        fontsize,
        backgroundColor,
        textColor: yColor
    };
    const zMaterial = {
        fontsize,
        backgroundColor,
        textColor: zColor
    };
    return {
        xMaterial,
        yMaterial,
        zMaterial
    };
};

class BoxHelper extends LineSegments {
    constructor(object, color = 0xffff00) {
        const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]);
        const positions = new Float32Array(8 * 3);

        const geometry = new BufferGeometry();
        geometry.setIndex(new BufferAttribute(indices, 1));
        geometry.setAttribute('position', new BufferAttribute(positions, 3));

        super(geometry, new LineBasicMaterial({ color, toneMapped: false }));

        this.object = object;
        this.type = 'BoxHelper';

        this.box = null;

        this.max = null;
        this.min = null;
        this.center = null;

        // drawBoundarySprite
        this.xSprite = null;
        this.ySprite = null;
        this.zSprite = null;
        this.spriteMesh = new Object3D();
        this.initSprite();

        this.update();
    }

    update = (callback) => {
        const position = this.geometry.attributes.position;
        const array = position.array;

        const executeAfterWorker = (max, min) => {
            this.box = {
                max: new Vector3(max.x, max.y, max.z),
                min: new Vector3(min.x, min.y, min.z),
                center: new Vector3((max.x + min.x) * 0.5, (max.y + min.y) * 0.5, (max.z + min.z) * 0.5)
            };

            this.object.box = this.box;

            this.max = max;
            this.min = min;

            /*
                 5____4
                1/___0/|
                | 6__|_7
                2/___3/
    
                0: max.x, max.y, max.z
                1: min.x, max.y, max.z
                2: min.x, min.y, max.z
                3: max.x, min.y, max.z
                4: max.x, max.y, min.z
                5: min.x, max.y, min.z
                6: min.x, min.y, min.z
                7: max.x, min.y, min.z
            */

            array[0] = max.x;
            array[1] = max.y;
            array[2] = max.z;
            array[3] = min.x;
            array[4] = max.y;
            array[5] = max.z;
            array[6] = min.x;
            array[7] = min.y;
            array[8] = max.z;
            array[9] = max.x;
            array[10] = min.y;
            array[11] = max.z;
            array[12] = max.x;
            array[13] = max.y;
            array[14] = min.z;
            array[15] = min.x;
            array[16] = max.y;
            array[17] = min.z;
            array[18] = min.x;
            array[19] = min.y;
            array[20] = min.z;
            array[21] = max.x;
            array[22] = min.y;
            array[23] = min.z;

            position.needsUpdate = true;

            this.updateSprite(this.box.max, this.box.min);
            if (callback) callback(max, min, this.object);
        };
        if (!this.object.geometry.json) return;
        workerGetMaxMinZValue(JSON.parse(this.object.geometry.json), [this.object.matrix], executeAfterWorker);
    };

    setFromObject(object, callback) {
        this.object = object;
        this.update(callback);
        return this;
    }

    updateByOffset = (offset, callback) => {
        const position = this.geometry.attributes.position;
        const array = position.array;

        const { max, min } = this.object.box;

        max.add(offset);
        min.add(offset);

        this.box = {
            max: new Vector3(max.x, max.y, max.z),
            min: new Vector3(min.x, min.y, min.z),
            center: new Vector3((max.x + min.x) * 0.5, (max.y + min.y) * 0.5, (max.z + min.z) * 0.5)
        };

        this.object.box = this.box;

        this.max = max;
        this.min = min;

        /*
                 5____4
                1/___0/|
                | 6__|_7
                2/___3/
    
                0: max.x, max.y, max.z
                1: min.x, max.y, max.z
                2: min.x, min.y, max.z
                3: max.x, min.y, max.z
                4: max.x, max.y, min.z
                5: min.x, max.y, min.z
                6: min.x, min.y, min.z
                7: max.x, min.y, min.z
            */

        array[0] = max.x;
        array[1] = max.y;
        array[2] = max.z;
        array[3] = min.x;
        array[4] = max.y;
        array[5] = max.z;
        array[6] = min.x;
        array[7] = min.y;
        array[8] = max.z;
        array[9] = max.x;
        array[10] = min.y;
        array[11] = max.z;
        array[12] = max.x;
        array[13] = max.y;
        array[14] = min.z;
        array[15] = min.x;
        array[16] = max.y;
        array[17] = min.z;
        array[18] = min.x;
        array[19] = min.y;
        array[20] = min.z;
        array[21] = max.x;
        array[22] = min.y;
        array[23] = min.z;

        position.needsUpdate = true;

        this.updateSprite(this.box.max, this.box.min);
        if (callback) callback(max, min, this.object);
    };

    setFromObjectByOffset = (object, offset = new Vector3(), callback = null) => {
        this.object = object;
        this.updateByOffset(offset, callback);
        return this;
    };

    copy(source, recursive) {
        super.copy(source, recursive);

        this.object = source.object;

        return this;
    }

    dispose() {
        this.geometry.dispose();
        this.material.dispose();

        this.xSprite.geometry.dispose();
        this.ySprite.geometry.dispose();
        this.zSprite.geometry.dispose();

        this.xSprite.material.dispose();
        this.ySprite.material.dispose();
        this.zSprite.material.dispose();

        this.spriteMesh = null;
        this.xSprite = null;
        this.ySprite = null;
        this.zSprite = null;
    }

    initSprite = (max = new Vector3(), min = new Vector3()) => {
        const { xMaterial, yMaterial, zMaterial } = getTextureMaterial();

        const value = max.clone().sub(min);

        const xSprite = drawTextSprite(`X:${value.x.toFixed(2)}`, xMaterial);
        const ySprite = drawTextSprite(`Y:${value.y.toFixed(2)}`, yMaterial);
        const zSprite = drawTextSprite(`Z:${value.z.toFixed(2)}`, zMaterial);

        const xPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(min.x, max.y, max.z)).multiplyScalar(0.5);
        const yPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(max.x, min.y, max.z)).multiplyScalar(0.5);
        const zPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(max.x, max.y, min.z)).multiplyScalar(0.5);

        xSprite.position.copy(xPosition);
        ySprite.position.copy(yPosition);
        zSprite.position.copy(zPosition);

        // xSprite.visible = false;
        // ySprite.visible = false;
        // zSprite.visible = false;

        this.xSprite = xSprite;
        this.ySprite = ySprite;
        this.zSprite = zSprite;

        this.spriteMesh.add(xSprite).add(ySprite).add(zSprite);
        this.add(this.spriteMesh);
    };

    updateSprite = (max, min) => {
        if (!this.xSprite || !this.ySprite || !this.zSprite) return;

        const value = max.clone().sub(min);

        const { xMaterial, yMaterial, zMaterial } = getTextureMaterial();

        const xTexture = createTexture(`X ${value.x.toFixed(2)}`, xMaterial).texture;
        const yTexture = createTexture(`Y ${value.y.toFixed(2)}`, yMaterial).texture;
        const zTexture = createTexture(`Z ${value.z.toFixed(2)}`, zMaterial).texture;

        this.xSprite.material.map = xTexture;
        this.ySprite.material.map = yTexture;
        this.zSprite.material.map = zTexture;

        const xPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(min.x, max.y, max.z)).multiplyScalar(0.5);
        const yPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(max.x, min.y, max.z)).multiplyScalar(0.5);
        const zPosition = new Vector3(max.x, max.y, max.z).add(new Vector3(max.x, max.y, min.z)).multiplyScalar(0.5);

        this.xSprite.position.copy(xPosition);
        this.ySprite.position.copy(yPosition);
        this.zSprite.position.copy(zPosition);
    };
}

export default BoxHelper;