triangulation_triangulate.js

import oc from "../opencascade/initializer";
import {Solid, Face, Edge, Wire} from "../modeling";

/**
 * Triangulates an entity (Solid, Face, Wire, or Edge) into a three.js compatible mesh.
 * @memberof triangulation
 * @alias triangulate
 * @param {Object} parameters - Triangulation parameters.
 * @param {Solid|Face|Wire|Edge} parameters.entity - The entity to triangulate.
 * @param {number} [parameters.linearTolerance=0.1] - The linear tolerance for triangulation (default is 0.1).
 * @param {number} [parameters.angularTolerance=0.1] - The angular tolerance for triangulation (default is 0.1).
 * @returns {Object} An object containing positions, normals, and indexes for the mesh.
 */
const triangulate = ({entity, linearTolerance = 0.1, angularTolerance = 0.1}) => {
  if (entity instanceof Solid) {
    return solidToMesh({solid: entity, linearTolerance, angularTolerance});
  } else if (entity instanceof Face) {
    return profileToMesh({profile: entity, linearTolerance, angularTolerance});
  } else if (entity instanceof Wire) {
    return wireToMesh({wire: entity, angularTolerance});
  } else if (entity instanceof Edge) {
    return edgeToMesh({edge: entity, angularTolerance});
  }
};

const solidToMesh = ({solid, linearTolerance, angularTolerance}) => {
  new oc.BRepMesh_IncrementalMesh_2(solid.wrapped, linearTolerance, false, angularTolerance, false);

  const positions = [];
  const normals = [];
  const indexes = [];

  solid.faces.forEach((face) => {
    const triangulation = triangulateFace({face, startIndex: positions.length / 3});
    positions.push(...triangulation.positions);
    normals.push(...triangulation.normals);
    indexes.push(...triangulation.indexes);
  });

  return {
    positions: new Float32Array(positions),
    normals: new Float32Array(normals),
    indexes: new Uint32Array(indexes),
  };
};

const profileToMesh = ({profile, linearTolerance, angularTolerance}) => {
  new oc.BRepMesh_IncrementalMesh_2(profile.wrapped, linearTolerance, false, angularTolerance, false);
  const triangulation = triangulateFace({face: profile, startIndex: 0});

  return {
    positions: new Float32Array(triangulation.positions),
    normals: new Float32Array(triangulation.normals),
    indexes: new Uint32Array(triangulation.indexes),
  };
};

const wireToMesh = ({wire, angularTolerance}) => {
  const positions = [];

  wire.edges.forEach((edge) => {
    const triangulation = triangulateEdge({edge, angularTolerance});
    positions.push(...triangulation.positions);
  });

  return {
    positions: new Float32Array(positions),
  };
};

const edgeToMesh = ({edge, angularTolerance}) => {
  return {positions: new Float32Array(triangulateEdge({edge, angularTolerance}).positions)};
};

const triangulateEdge = ({edge, angularTolerance}) => {
  const positions = [];

  const curve = new oc.BRepAdaptor_Curve_2(edge.wrapped);
  const tangentialDeflection = new oc.GCPnts_TangentialDeflection_2(curve, angularTolerance, angularTolerance, 2, 1e-9, 1e-7);
  for (let i = 1; i < tangentialDeflection.NbPoints(); i++) {
    const previous = tangentialDeflection.Value(i);
    const current = tangentialDeflection.Value(i + 1);
    positions.push(previous.X(), previous.Y(), previous.Z());
    positions.push(current.X(), current.Y(), current.Z());
  }

  return {positions: positions};
};

const triangulateFace = ({face, startIndex}) => {
  const positions = [];
  const normals = [];
  const indexes = [];

  let location = new oc.TopLoc_Location_1();
  // the next line also updates the value of the location
  let triangulation = oc.BRep_Tool.Triangulation(face.wrapped, location, 0);
  // this updates the triangulation object with normals
  // oc.Poly.ComputeNormals(triangulation);
  const transformation = location.Transformation();
  const triangles = triangulation.get();
  triangles.ComputeNormals();

  const nodesLength = triangles.NbNodes();
  for (let index = 0; index < nodesLength; index++) {
    const point = triangles.Node(index + 1).Transformed(transformation);
    positions[index * 3 + 0] = point.X();
    positions[index * 3 + 1] = point.Y();
    positions[index * 3 + 2] = point.Z();

    const direction = triangles.Normal_1(index + 1).Transformed(transformation);
    if (!(face.wrapped.Orientation_1() === oc.TopAbs_Orientation.TopAbs_FORWARD)) {
      direction.Reverse();
    }
    normals[index * 3 + 0] = direction.X();
    normals[index * 3 + 1] = direction.Y();
    normals[index * 3 + 2] = direction.Z();
  }

  const trianglesLength = triangles.NbTriangles();
  for (let index = 0; index < trianglesLength; index++) {
    const triangle = triangles.Triangle(index + 1);
    let n1 = triangle.Value(1);
    let n2 = triangle.Value(2);
    let n3 = triangle.Value(3);
    if (!(face.wrapped.Orientation_1() === oc.TopAbs_Orientation.TopAbs_FORWARD)) {
      [n1, n2] = [n2, n1]; // swap n1 and n2 values
    }
    indexes[index * 3 + 0] = n1 - 1 + startIndex;
    indexes[index * 3 + 1] = n2 - 1 + startIndex;
    indexes[index * 3 + 2] = n3 - 1 + startIndex;
  }

  return {positions, normals, indexes};
};

export {triangulate};