SupportMesh.js 6.8 KB
import { Vector3, SphereGeometry, CylinderGeometry, Matrix4, Quaternion } from 'three';
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';

const EEndpointType = {
    eetNone: 0,
    eetTouch: 1,
    eetLink: 2,
    eetCone: 3,
    eetCylinder: 4,
    eetFork: 5,
    eetBranch: 6,
    eetStrong: 7,
    eetSmall: 8,
    eetBase: 9,
    eetFloor: 10
};

const generateCgSphere = (position, radius, precision) => {
    const geometry = new SphereGeometry(radius, precision, precision);
    const matrix = new Matrix4().makeTranslation(position);
    const rotateMatrix = new Matrix4().makeRotationX((Math.PI / 180) * 90);
    geometry.applyMatrix4(rotateMatrix);
    geometry.applyMatrix4(matrix);
    return geometry;
};

const generateCgCylinder = (topCenter, bottomCenter, bottomRadius, topRadius, precision) => {
    const height = bottomCenter.distanceTo(topCenter);

    const geometry = new CylinderGeometry(topRadius, bottomRadius, height, precision, precision);
    const originNormal = new Vector3(0, 1, 0);
    const normal = bottomCenter.clone().sub(topCenter).normalize();

    const center = bottomCenter.clone().add(topCenter).multiplyScalar(0.5);
    const q = new Quaternion().setFromUnitVectors(originNormal, normal);
    const matrix = new Matrix4().compose(center, q, new Vector3(1, 1, 1));
    geometry.applyMatrix4(matrix);
    return geometry;
};

class SupportMesh {
    constructor() {
        this.supports = [];
        this.parameters = {
            touchDepth: 0.5,
            precision: 8
        };
    }

    setSupports = (supports) => {
        this.supports = supports;
        // console.log(this.supports);
    };

    setParameters = (parameters) => {
        Object.entries(parameters).forEach(([key, value]) => {
            if (Math.abs(value) > 0.000001) {
                this.parameters[key] = value;
            }
        });
    };

    getNode = (id, sid) => {
        if (this.supports.length === 0) return null;
        const node = this.supports.find((el) => el.id === id && el.sid === sid);
        return node;
    };

    getNonNode = (id) => {
        if (this.supports.length === 0) return null;
        const node = this.supports.find((el) => el.id === id);
        return node;
    };

    updateGeometry = ({ ele }) => {
        const { precision, touchDepth } = this.parameters;

        const geometrys = [];

        const { type, position, normal, radius, id, parentId, nextId, sid } = ele;

        const node = this.getNode(id, sid);
        if (!node) return null;

        let parentNode = this.getNode(parentId, sid);
        let nextNode = this.getNode(nextId, sid);

        if (!parentNode) parentNode = this.getNonNode(parentId);
        if (!nextNode) nextNode = this.getNonNode(nextId);

        let pos = position;
        switch (type) {
            case EEndpointType.eetTouch:
            case EEndpointType.eetLink:
                if (type === EEndpointType.eetTouch) {
                    pos = position.clone().add(normal.clone().multiplyScalar(radius - touchDepth));
                }
                geometrys.push(generateCgSphere(pos, radius, precision));
                break;
            case EEndpointType.eetCone:
            case EEndpointType.eetCylinder:
            case EEndpointType.eetSmall:
            case EEndpointType.eetBranch:
            case EEndpointType.eetFork:
            case EEndpointType.eetBase:
            case EEndpointType.eetStrong:
                if (nextNode) {
                    if (type === EEndpointType.eetCone) {
                        if (parentNode && parentNode.type === EEndpointType.eetTouch) {
                            pos = parentNode.position
                                .clone()
                                .add(parentNode.normal.clone().multiplyScalar(parentNode.radius - touchDepth));
                            geometrys.push(
                                generateCgCylinder(pos, nextNode.position, radius, nextNode.radius, precision)
                            );
                        } else {
                            const nextNextNode = this.getNode(nextNode.nextId, sid);
                            if (nextNextNode) {
                                if (nextNextNode.type === EEndpointType.eetTouch) {
                                    pos = nextNextNode.position
                                        .clone()
                                        .add(
                                            nextNextNode.normal.clone().multiplyScalar(nextNextNode.radius - touchDepth)
                                        );
                                    geometrys.push(
                                        generateCgCylinder(position, pos, radius, nextNode.radius, precision)
                                    );
                                }
                            }
                        }
                    } else if (type === EEndpointType.eetSmall) {
                        let spos = pos;
                        if (parentNode && parentNode.type === EEndpointType.eetTouch) {
                            spos = parentNode.position
                                .clone()
                                .add(parentNode.normal.clone().multiplyScalar(parentNode.radius - touchDepth));
                        }
                        if (nextNode) {
                            let epos = nextNode.position;
                            if (nextNode.type === EEndpointType.eetNone) {
                                const nextNextNode = this.getNode(nextNode.nextId, sid);
                                if (nextNextNode && nextNextNode.type === EEndpointType.eetTouch) {
                                    epos = nextNextNode.position
                                        .clone()
                                        .add(
                                            nextNextNode.normal.clone().multiplyScalar(nextNextNode.radius - touchDepth)
                                        );
                                }
                            } else if (nextNode.type === EEndpointType.eetTouch) {
                                epos = nextNode.position
                                    .clone()
                                    .add(nextNode.normal.clone().multiplyScalar(nextNode.radius - touchDepth));
                            }
                            geometrys.push(generateCgCylinder(spos, epos, radius, nextNode.radius, precision));
                        }
                    } else {
                        geometrys.push(generateCgCylinder(pos, nextNode.position, radius, nextNode.radius, precision));
                    }
                }
                break;
            default:
                break;
        }
        if (geometrys.length === 0) return null;
        const geometry = mergeGeometries(geometrys);
        return geometry;
    };
}
export default SupportMesh;