Three.js 根据多点坐标生成一个具有宽度的(二维)线

219 阅读2分钟
  • Mock一条线段

const points = [
    [0,0,0],
    [1,0,1],
    [3,0,5],
    [5,0,15],
    [10,0,20]
]

  • 根据线段生成平滑曲线(二维向量)
const __points = lines.map((p: Ros3Array) => {
  return new Vector2(p[0], p[2]);
});


let curve = new SplineCurve(__points);
let _points = curve.getPoints(50); // 生成50个点
  • 计算缓冲几何体(BufferGeometry)需要的数据
// 获取 BufferAttrs
const getBufferData = (_points: Vector2[]) => {
    let _ps: Ros3Array[] = [];
    let _indexesArray: number[] = [];

    // 计算顶点
    for (let i = 0, r = 1; r < _points.length; i++, r++) {
        const start = newPoint[i];
        const end = newPoint[r];
        if (start.equals(end)) {
          continue;
        }
        const se = end.clone().sub(start.clone()); // 从s到e;
        const oew = se.clone().setLength(radius); // 从原点开始的se斜率向量的w长度;
        const sew = oew.clone().add(start.clone()); // 从start开始的se斜率向量的w长度;
        const len = sew.clone().rotateAround(start.clone(), degToRad(90)); // s到sew向量逆时针旋转90度
        const den = sew.clone().rotateAround(start.clone(), degToRad(-90)); // s到sew向量顺时针旋转90度

        _ps.push([len.x, 0, len.y], [den.x, 0, den.y]);
        if (r === newPoint.length - 1) {
          const eew = oew.clone().add(end.clone()); // 从end开始的se斜率向量的w长度;
          const lEnd = eew.clone().rotateAround(end.clone(), degToRad(90)); // e到eew向量逆时针旋转90度
          const rEnd = eew.clone().rotateAround(end.clone(), degToRad(-90)); // e到eew向量顺时针旋转90度
          _ps.push([lEnd.x, 0, lEnd.y], [rEnd.x, 0, rEnd.y]);
        }
      }


    // 计算顶点索引
    for (let i = 0, r = 1; r < newPoint.length; i++, r++) {
        if (_i > 0) {
          let n = (_i - 1) * 2;
          let n0 = n;
          let n1 = n + 1;
          let n2 = n + 2;
          let n3 = n + 3;
          let veticle0 = [_ps[n0], _ps[n1], _ps[n2]] as const;
          let veticle2 = [_ps[n3], _ps[n2], _ps[n1]] as const;
          const clockwise = getIsCounterClockwiseBy3Ros3Array(...veticle0); // 是否顺时针
          const clockwise2 = getIsCounterClockwiseBy3Ros3Array(...veticle2); // 是否顺时针
          const index = clockwise ? [n0, n1, n2] : [n0, n2, n1];
          const index2 = clockwise2 ? [n3, n2, n1] : [n3, n1, n2];
          _indexesArray.push(...index, ...index2);
        }
        _i++;
    }


    let __ps = _ps.flat();
    const ps = new Float32Array(__ps);
    const indexesArray = new Uint16Array(_indexesArray);


    return {
        ps: Comlink.transfer(ps, [ps.buffer]), // Worker线程计算并输出
        indexesArray: Comlink.transfer(indexesArray, [indexesArray.buffer]) // Worker线程计算并输出
    };
}

  • 创建宽线物体

const { ps, indexesArray } = getBufferData(_points);

let geometry = new BufferGeometry();

geometry.index = new BufferAttribute(indexesArray, 1);
geometry.setAttribute('position', new BufferAttribute(ps, 3));

const m = new MeshBasicMaterial({
    side: BackSide,
    transparent: true,
    alphaTest: 0.1,
    color: '#f00'
});
m.needsUpdate = true;
const mesh = new Mesh(geometry, m);

this.scene.add(mesh)