DrawMargin.js 7.36 KB
import { CatmullRomCurve3, TubeGeometry, Mesh, Object3D, SphereGeometry, MeshStandardMaterial, Vector3 } from 'three';
import * as THREE from 'three';

const drawPoints = (points, color = 0x000000, radius = 0.1) => {
    const object = new Object3D();
    let i = 0;
    while (i < points.length) {
        const { point, originPointIndex } = points[i];
        const geometry = new SphereGeometry(radius, 32, 16);
        const material = new MeshStandardMaterial({ color });
        const sphere = new Mesh(geometry, material);
        sphere.position.set(point.x, point.y, point.z);
        sphere.name = originPointIndex;
        points[i].mesh = sphere;
        object.add(sphere);
        ++i;
    }
    return object;
};

const getAveragePoints = (p1, p2, numPoints) => {
    const points = [];
    for (let i = 0; i < numPoints; i++) {
        const t = i / (numPoints - 1);
        if (t < 1) {
            const point = new Vector3().lerpVectors(p1, p2, t);
            points.push(point);
        }
    }
    return points;
};

const lerpPoints = (points_) => {
    const points = [];
    for (let i = 0; i < points_.length - 1; i++) {
        const p1 = points_[i];
        const p2 = points_[i + 1];
        const distance = p1.distanceTo(p2);
        if (distance > 0.6) {
            const pts = getAveragePoints(p1, p2, Math.round(distance / 0.3) + 1);
            pts.forEach((pt) => {
                points.push(pt);
            });
        } else if (distance < 0.3) {
            points.push(p1);
        } else {
            points.push(p1);
            points.push(p2);
        }
    }
    return points;
};

class DrawMargin {
    constructor({ scene }) {
        this._scene = scene;

        this.originPoints = [];
        this.controlPoints = [];

        this.controlOnlyPoints = [];

        this.curveMesh = null;
        this.controlPointsMesh = null;
        this.marginGroup = null;

        this.outputPoints = [];
        this.curvePoints = [];
    }

    load = (points, mesh, isAI = false, callback = null) => {
        const points_ = isAI ? lerpPoints(points) : points;

        if (mesh) {
            points_.forEach((el) => {
                const a = {};
                const target = mesh.geometry.boundsTree.closestPointToPoint(el, a).point;
                el.copy(target);
            });
        }

        this.outputPoints = [...points_];
        this.originPoints = points_;

        this.controlPointsMesh = this.createControlPointsMesh(points_);
        this.controlPointsMesh.name = 'control-points';

        for (let i = 0; i < this.controlPoints.length; i++) {
            this.controlOnlyPoints.push(this.controlPoints[i].point);
        }

        this.curveMesh = this.createCurveMesh(this.controlOnlyPoints, mesh);

        const group = new Object3D();
        group.name = 'trimline';

        // this._scene.add(this.curveMesh);
        // this._scene.add(this.controlPointsMesh);

        group.add(this.curveMesh);
        group.add(this.controlPointsMesh);
        group.matrixAutoUpdate = false;
        group.matrixWorldNeedsUpdate = true;
        this.marginGroup = group;
        this._scene.add(this.marginGroup);

        if (callback) callback();
    };

    setToWorld = (mesh = null) => {
        const points = [];
        this.originPoints.forEach((el) => {
            points.push(el.clone().applyMatrix4(this.marginGroup.matrix));
        });
        const object = this._scene.getObjectByName('trimline');
        if (object) {
            this._scene.remove(object);
            this.controlPointsMesh = this.createControlPointsMesh(points);
            this.controlPointsMesh.name = 'control-points';

            this.controlOnlyPoints = [];

            for (let i = 0; i < this.controlPoints.length; i++) {
                this.controlOnlyPoints.push(this.controlPoints[i].point);
            }
            this.curveMesh = this.createCurveMesh(this.controlOnlyPoints, mesh);
            this.marginGroup = new Object3D();
            this.marginGroup.add(this.curveMesh);
            this.marginGroup.add(this.controlPointsMesh);

            this._scene.add(this.marginGroup);
        }
    };

    createCurveMesh = (points, mesh = null) => {
        const curve = new CatmullRomCurve3(points, true);
        curve.curveType = 'chordal';
        this.fittedCurve = curve;

        const pts = curve.getPoints(500);

        if (mesh) {
            pts.forEach((el) => {
                const a = {};
                const target = mesh.geometry.boundsTree.closestPointToPoint(el, a).point;
                el.copy(target);
            });
        }

        const curve1 = new CatmullRomCurve3(pts, true);
        this.outputPoints = curve1.getPoints(100);
        this.curvePoints = curve1.getPoints(pts.length * 3);

        const geometry = new TubeGeometry(curve1, pts.length * 2, 0.1, 100, true);
        const material = new MeshStandardMaterial({
            color: 0xff5809,
            roughness: 0.5
        });
        const curveObject = new THREE.Mesh(geometry, material);
        /*
        const pts = curve.getPoints(1000);
        const geometry = new THREE.BufferGeometry().setFromPoints(pts);
        const material = new THREE.LineBasicMaterial({ color: 0xff5809 });
        const curveObject = new THREE.Line(geometry, material);
        */
        curveObject.name = 'trimline';

        return curveObject;
    };

    createControlPointsMesh = (points) => {
        const controlPoints = [];
        for (let i = 0; i < points.length - 1; i += Math.round(points.length / 100)) {
            controlPoints.push({ point: points[i], originPointIndex: i, mesh: null });
        }
        this.controlPoints = controlPoints;
        const ctrlMesh = drawPoints(controlPoints, 0xff5809, 0.4);
        return ctrlMesh;
    };

    createNewControlPointsMesh = (points) => {
        const controlPoints = [];
        for (let i = 0; i < points.length - 1; i += 1) {
            controlPoints.push({ point: points[i], originPointIndex: i, mesh: null });
        }
        this.controlPoints = controlPoints;
        const ctrlMesh = drawPoints(controlPoints, 0xff5809, 0.4);
        return ctrlMesh;
    };

    updateCurveMesh = (points, mesh) => {
        // 更新曲線點

        const curve = new CatmullRomCurve3(points, true);

        const pts = curve.getPoints(500);

        pts.forEach((el) => {
            const a = {};
            const target = mesh.geometry.boundsTree.closestPointToPoint(el, a).point;
            el.copy(target);
        });

        const curve1 = new CatmullRomCurve3(pts, true);
        this.outputPoints = curve1.getPoints(100);
        this.curvePoints = curve1.getPoints(pts.length * 3);

        curve.curveType = 'chordal';
        const newGeometry = new TubeGeometry(curve1, pts.length * 2, 0.1, 100, true);
        this.curveMesh.geometry.dispose();
        this.curveMesh.geometry = newGeometry;

        // const object = this._scene.getObjectByName('line');
        // this._scene.remove(object);
        // this._scene.add(this.curveMesh);
    };

    updateControlPoints = (points) => {
        this.controlPointsMesh = this.createNewControlPointsMesh(points);
        this.controlPointsMesh.name = 'control-points';
        this.controlPointsMesh.visible = false;
        const objects = this._scene.getObjectByName('control-points');
        if (objects) this._scene.remove(objects);
        this._scene.add(this.controlPointsMesh);
    };
}

export default DrawMargin;