@bitbybit-dev/occt
Version:
Bit By Bit Developers CAD algorithms using OpenCascade Technology kernel. Run in Node and in Browser.
254 lines (253 loc) • 11.9 kB
JavaScript
export class MeshingService {
constructor(occ, shapeGettersService, transformsService, edgesService, facesService, wiresService, base) {
this.occ = occ;
this.shapeGettersService = shapeGettersService;
this.transformsService = transformsService;
this.edgesService = edgesService;
this.facesService = facesService;
this.wiresService = wiresService;
this.base = base;
}
shapeFacesToPolygonPoints(inputs) {
const def = this.shapeToMesh(inputs);
const res = [];
def.faceList.forEach(face => {
const vertices = face.vertex_coord;
const indices = face.tri_indexes;
for (let i = 0; i < indices.length; i += 3) {
const p1 = indices[i];
const p2 = indices[i + 1];
const p3 = indices[i + 2];
let pts = [
[vertices[p1 * 3], vertices[p1 * 3 + 1], vertices[p1 * 3 + 2]],
[vertices[p2 * 3], vertices[p2 * 3 + 1], vertices[p2 * 3 + 2]],
[vertices[p3 * 3], vertices[p3 * 3 + 1], vertices[p3 * 3 + 2]],
];
if (inputs.reversedPoints) {
pts = pts.reverse();
}
res.push(pts);
}
});
return res;
}
shapesToMeshes(inputs) {
return inputs.shapes.map(shape => this.shapeToMesh({ shape, precision: inputs.precision, adjustYtoZ: inputs.adjustYtoZ }));
}
shapeToMesh(inputs) {
const faceList = [];
const edgeList = [];
const pointsList = [];
let shapeToUse = inputs.shape;
if (shapeToUse.IsNull())
return { faceList, edgeList, pointsList: [] };
// This could be made optional...
// Clean cached triangulation data for the shape.
// This allows to get lower res models out of higher res that was once computed and cached.
this.occ.BRepTools.Clean(shapeToUse, true);
if (inputs.adjustYtoZ) {
const shapeToUseRotated = this.transformsService.rotate({ shape: inputs.shape, axis: [1, 0, 0], angle: -90 });
const shapeMirrored = this.transformsService.mirrorAlongNormal({ shape: shapeToUseRotated, origin: [0, 0, 0], normal: [0, 0, 1] });
shapeToUseRotated.delete();
shapeToUse.delete();
shapeToUse = shapeMirrored;
}
// Iterate through the faces and triangulate each one
const triangulations = [];
const faces = this.shapeGettersService.getFaces({ shape: shapeToUse });
let incrementalMeshBuilder;
if (faces && faces.length) {
incrementalMeshBuilder = new this.occ.BRepMesh_IncrementalMesh_2(shapeToUse, inputs.precision, false, 0.5, false);
faces.forEach((myFace, faceIndex) => {
const aLocation = new this.occ.TopLoc_Location_1();
const myT = this.occ.BRep_Tool.Triangulation(myFace, aLocation, 0);
if (myT.IsNull()) {
console.error("Encountered Null Face!");
return;
}
const thisFace = {
vertex_coord: [],
normal_coord: [],
uvs: [],
tri_indexes: [],
vertex_coord_vec: [],
number_of_triangles: 0,
center_point: undefined,
center_normal: undefined,
face_index: faceIndex
};
thisFace.center_point = this.facesService.pointOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 });
thisFace.center_normal = this.facesService.normalOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 });
const pc = new this.occ.Poly_Connect_2(myT);
const triangulation = myT.get();
// write vertex buffer
thisFace.vertex_coord = new Array(triangulation.NbNodes() * 3);
thisFace.vertex_coord_vec = [];
for (let i = 0; i < triangulation.NbNodes(); i++) {
const p = triangulation.Node(i + 1).Transformed(aLocation.Transformation());
const uv = triangulation.UVNode(i + 1);
thisFace.uvs[(i * 2) + 0] = uv.X();
thisFace.uvs[(i * 2) + 1] = uv.Y();
thisFace.vertex_coord[(i * 3) + 0] = p.X();
thisFace.vertex_coord[(i * 3) + 1] = p.Y();
thisFace.vertex_coord[(i * 3) + 2] = p.Z();
thisFace.vertex_coord_vec.push([p.X(), p.Y(), p.Z()]);
}
// write normal buffer
const myNormal = new this.occ.TColgp_Array1OfDir_2(1, triangulation.NbNodes());
this.occ.StdPrs_ToolTriangulatedShape.Normal(myFace, pc, myNormal);
thisFace.normal_coord = new Array(myNormal.Length() * 3);
for (let i = 0; i < myNormal.Length(); i++) {
const d = myNormal.Value(i + 1);
thisFace.normal_coord[(i * 3) + 0] = d.X();
thisFace.normal_coord[(i * 3) + 1] = d.Y();
thisFace.normal_coord[(i * 3) + 2] = d.Z();
}
// write triangle buffer
const orient = myFace.Orientation_1();
const triangles = myT.get().Triangles();
thisFace.tri_indexes = new Array(triangles.Length() * 3);
let validFaceTriCount = 0;
for (let nt = 1; nt <= myT.get().NbTriangles(); nt++) {
const t = triangles.Value(nt);
let n1 = t.Value(1);
let n2 = t.Value(2);
const n3 = t.Value(3);
if (orient !== this.occ.TopAbs_Orientation.TopAbs_FORWARD) {
const tmp = n1;
n1 = n2;
n2 = tmp;
}
thisFace.tri_indexes[(validFaceTriCount * 3) + 0] = n1 - 1;
thisFace.tri_indexes[(validFaceTriCount * 3) + 1] = n2 - 1;
thisFace.tri_indexes[(validFaceTriCount * 3) + 2] = n3 - 1;
validFaceTriCount++;
}
thisFace.number_of_triangles = validFaceTriCount;
faceList.push(thisFace);
triangulations.push(myT);
aLocation.delete();
myNormal.delete();
triangles.delete();
pc.delete();
});
}
// Nullify Triangulations between runs so they're not stored in the cache
for (let i = 0; i < triangulations.length; i++) {
triangulations[i].Nullify();
triangulations[i].delete();
}
// Get the free edges that aren't on any triangulated face/surface
const edges = this.shapeGettersService.getEdges({ shape: shapeToUse });
edges.forEach((myEdge, index) => {
const thisEdge = {
vertex_coord: [],
middle_point: undefined,
edge_index: -1
};
thisEdge.middle_point = this.edgesService.pointOnEdgeAtParam({ shape: myEdge, param: 0.5 });
const aLocation = new this.occ.TopLoc_Location_1();
const adaptorCurve = new this.occ.BRepAdaptor_Curve_2(myEdge);
const tangDef = new this.occ.GCPnts_TangentialDeflection_2(adaptorCurve, inputs.precision, 0.1, 2, 1.0e-9, 1.0e-7);
// write vertex buffer
thisEdge.vertex_coord = [];
const nrPoints = tangDef.NbPoints();
const tangDefValues = [];
for (let j = 0; j < nrPoints; j++) {
const tangDefVal = tangDef.Value(j + 1);
thisEdge.vertex_coord.push([
tangDefVal.X(),
tangDefVal.Y(),
tangDefVal.Z()
]);
tangDefValues.push(tangDefVal);
}
thisEdge.edge_index = index;
edgeList.push(thisEdge);
tangDefValues.forEach(v => v.delete());
aLocation.delete();
adaptorCurve.delete();
tangDef.delete();
this.occ.BRepTools.Clean(myEdge, true);
});
const vertices = this.shapeGettersService.getVertices({ shape: shapeToUse });
if (vertices.length > 0) {
vertices.forEach(v => {
const pt = this.occ.BRep_Tool.Pnt(v);
pointsList.push([pt.X(), pt.Y(), pt.Z()]);
pt.delete();
});
}
if (incrementalMeshBuilder) {
incrementalMeshBuilder.Delete();
}
this.occ.BRepTools.Clean(shapeToUse, true);
return { faceList, edgeList, pointsList };
}
meshMeshIntersectionWires(inputs) {
const shape1 = inputs.shape1;
const shape2 = inputs.shape2;
const mesh1 = this.shapeFacesToPolygonPoints({ shape: shape1, precision: inputs.precision1, adjustYtoZ: false, reversedPoints: false });
const mesh2 = this.shapeFacesToPolygonPoints({ shape: shape2, precision: inputs.precision2, adjustYtoZ: false, reversedPoints: false });
const res = this.base.mesh.meshMeshIntersectionPolylines({
mesh1, mesh2
});
const wires = [];
res.forEach(r => {
if (r.points && r.points.length > 0) {
if (r.isClosed) {
wires.push(this.wiresService.createPolygonWire({
points: r.points,
}));
}
else {
wires.push(this.wiresService.createPolylineWire({
points: r.points,
}));
}
}
});
return wires;
}
meshMeshIntersectionPoints(inputs) {
const shape1 = inputs.shape1;
const shape2 = inputs.shape2;
const mesh1 = this.shapeFacesToPolygonPoints({ shape: shape1, precision: inputs.precision1, adjustYtoZ: false, reversedPoints: false });
const mesh2 = this.shapeFacesToPolygonPoints({ shape: shape2, precision: inputs.precision2, adjustYtoZ: false, reversedPoints: false });
return this.base.mesh.meshMeshIntersectionPoints({ mesh1, mesh2 });
}
meshMeshIntersectionOfShapesWires(inputs) {
const wireIntersections = [];
inputs.shapes.forEach((shape, index) => {
const shape1 = inputs.shape;
const shape2 = inputs.shapes[index];
let precision2 = inputs.precision;
if (inputs.precisionShapes && inputs.precisionShapes.length > 0) {
const p = inputs.precisionShapes[index];
if (p) {
precision2 = p;
}
}
const wires = this.meshMeshIntersectionWires({ shape1, shape2, precision1: inputs.precision, precision2 });
wireIntersections.push(...wires);
});
return wireIntersections;
}
meshMeshIntersectionOfShapesPoints(inputs) {
const pointIntersections = [];
inputs.shapes.forEach((shape, index) => {
const shape1 = inputs.shape;
const shape2 = inputs.shapes[index];
let precision2 = inputs.precision;
if (inputs.precisionShapes && inputs.precisionShapes.length > 0) {
const p = inputs.precisionShapes[index];
if (p) {
precision2 = p;
}
}
const points = this.meshMeshIntersectionPoints({ shape1, shape2, precision1: inputs.precision, precision2 });
pointIntersections.push(...points);
});
return pointIntersections;
}
}