UNPKG

@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
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; } }