UNPKG

holes-in

Version:

Generates a 3D mesh from a 2D outer path and 2D inner paths

261 lines (213 loc) 8.8 kB
"use strict"; const pathHelper = require("./path-helper.js"); const constants = require("./constants.js"); const geomHelper = { mergeMeshes(geoms) { let res = geomHelper.getEmptyGeom(); for (let i = 0; i < geoms.length; i++) { if(Array.isArray(geoms[i])) { res = geomHelper.mergeMesh(res, geomHelper.mergeMeshes(geoms[i])); continue; } res = geomHelper.mergeMesh(res, geoms[i]); } return res; }, mergeMesh(geom1, geom2) { if (!geom2) return geom1; if (!geom1) return geom2; const offset = geom1.points.length / 3; geom1.faces.push(...geom2.faces.map(f => f + offset)); geom1.points.push(...geom2.points); geom1.normals.push(...geom2.normals); if (geom2.uvs) { geom1.uvs.push(...geom2.uvs); } if (geom2.uvs2) { geom1.uvs2.push(...geom2.uvs2); } return geom1; }, /* * Returns two triangles representing the larger face we can build from the edge ptDwn->nPtDwn */ getOneVerticalGeom(idxPtDwn, nIdxPtDwn, indexDepthDwn, pathDwn, pathsByDepth, offset = 0, invertNormal = false) { const ptDwn = pathDwn[idxPtDwn]; const nPtDwn = pathDwn[nIdxPtDwn]; if (ptDwn._holesInForbidden) { return; } const indexDepthUp = geomHelper.getMatchDepths(ptDwn, nPtDwn, +indexDepthDwn, pathsByDepth); if (indexDepthUp === undefined || indexDepthUp < 0) { return; } const depthUp = pathsByDepth[indexDepthUp].depth; const depthDwn = pathsByDepth[indexDepthDwn].depth; const res = geomHelper.getPtsNormsIndx2d(ptDwn, nPtDwn, depthUp, depthDwn, +offset, invertNormal); res.uvs = []; // add uvs: geomHelper.addUvsToVertGeom(res, +idxPtDwn, pathDwn, pathsByDepth, indexDepthDwn, indexDepthUp); return Object.assign(geomHelper.getEmptyGeom(), res); }, /** * Returns the depths at which they are two edges with the same 2D coords. * If it does not exists such a edge, returns the current depth and the depth above */ getMatchDepths(ptDwn, nPtDwn, indexDepth, pathsByDepth) { // for each depth deeper than pathUp,we look for a corresponding point: let res = indexDepth - 1; for (let i = indexDepth - 1; i >= 0; i--) { const pathsAtDepth = pathsByDepth[i].paths; if (!pathsAtDepth) { continue; } for (let j = 0; j < pathsAtDepth.length; j++) { // for each path at each depth: const pathUp = pathsAtDepth[j]; const match1 = pathHelper.getPointMatch(pathUp, ptDwn); const match2 = pathHelper.getPointMatch(pathUp, nPtDwn); const perfectMatch = match1 && match2 && (match1.index + 1) % pathUp.length === match2.index; if (!match1) { continue; } if (pathsByDepth[i].paths[j][match1.index]._holesInVisited) { return; } pathsByDepth[i].paths[j][match1.index]._holesInVisited = true; if (perfectMatch) { res = i; continue; } else { return i; } } } return res; }, markAsVisited(pointA, pointB, toMarkPaths, depth) { if (!toMarkPaths) { return; } for (let i = depth; i >= 0; i--) { const paths = toMarkPaths[i].paths; for (let j = 0; j < paths.length; j++) { const match1 = pathHelper.getPointMatch(paths[j], pointA); const match2 = pathHelper.getPointMatch(paths[j], pointB); if (!match1 || !match2) { continue; } paths[j][match1.index]._holesInForbidden = true; } } }, getPtsNormsIndx2d(point2d1, point2d2, depthUp, depthDwn, offset, invertNormal = false) { const point1 = geomHelper.getPoint3(point2d1, depthUp); const point2 = geomHelper.getPoint3(point2d1, depthDwn); const point3 = geomHelper.getPoint3(point2d2, depthDwn); const point4 = geomHelper.getPoint3(point2d2, depthUp); return geomHelper.getPtsNormsIndx3d([point1, point2, point3, point4], +offset, invertNormal); }, getPtsNormsIndx3d(points3d, offset, invertNormal) { let resFaces; let normal; if (invertNormal) { resFaces = ([0, 1, 2, 0, 2, 3]).map(elt => elt + offset); normal = geomHelper.getNormalToPlan(points3d[0], points3d[1], points3d[2]); } else { resFaces = ([2, 1, 0, 3, 2, 0]).map(elt => elt + offset); normal = geomHelper.getNormalToPlan(points3d[2], points3d[1], points3d[0]); } const resNorm = []; resNorm.push(...normal); resNorm.push(...normal); resNorm.push(...normal); resNorm.push(...normal); const resPoints = []; resPoints.push(...points3d[0]); resPoints.push(...points3d[1]); resPoints.push(...points3d[2]); resPoints.push(...points3d[3]); return { points: resPoints, normals: resNorm, faces: resFaces, offset: offset + 4, }; }, addUvsToVertGeom(geom, indexPtDwn, pathDwn, pathsByDepth, indexDepth, indexDepthUp) { const vUp = pathsByDepth[indexDepthUp].holesInV; const vDwn = pathsByDepth[indexDepth].holesInV; const nIndexPtDwn = (indexPtDwn + 1) % pathDwn.length; const u = pathDwn[indexPtDwn].holesInU; let nu = pathDwn[nIndexPtDwn].holesInU; if (pathDwn[nIndexPtDwn].holesInU2) { nu = pathDwn[nIndexPtDwn].holesInU2; } const uvs = [u, vUp, u, vDwn, nu, vDwn, nu, vUp]; geom.uvs.push(...uvs); }, getHorizontalGeom(triangles, offset = 0, invertNormal) { if (triangles.triangles.length > 0) { const currGeom = geomHelper.getGeomFromTriangulation(triangles, +triangles.depth, invertNormal, offset); offset = currGeom.offset; return currGeom; } }, getGeomFromTriangulation(triangles, depth, invertNormal = false) { const points = []; for (let i = 0; i < triangles.points.length; i++) { points.push(triangles.points[i][0] / constants.scaleFactor); points.push(triangles.points[i][1] / constants.scaleFactor); points.push(depth); } if (!invertNormal) { for (let i = 0; i < triangles.triangles.length; i++) { triangles.triangles[i].reverse(); } } // offsets faces const faces = []; for (let i = 0; i < triangles.triangles.length; i++) { faces.push(...triangles.triangles[i].map(index => index)); // eslint-disable-line } // offset += triangles.points.length; // get normals: const normals = []; const idxs = triangles.triangles[0].map(elt => elt * 3); const pt1 = points.slice(idxs[0], idxs[0] + 3); const pt2 = points.slice(idxs[1], idxs[1] + 3); const pt3 = points.slice(idxs[2], idxs[2] + 3); const normal = geomHelper.getNormalToPlan(pt1, pt2, pt3); for (let i = 0; i < triangles.points.length; i++) { // eslint-disable-line normals.push(...normal); } return Object.assign(geomHelper.getEmptyGeom(), { points, faces, normals, }); }, getNormalToPlan(point1, point2, point4) { const vec1 = geomHelper.pointsToVec(point1, point2); const vec2 = geomHelper.pointsToVec(point1, point4); return geomHelper.normalizeVec(geomHelper.cross(vec2, vec1)); }, pointsToVec(point1, point2) { return [point2[0] - point1[0], point2[1] - point1[1], point2[2] - point1[2]]; }, getPoint3(point2, depth) { return [point2.X / constants.scaleFactor, point2.Y / constants.scaleFactor, depth]; }, cross(vec1, vec2) { return [vec1[1] * vec2[2] - vec1[2] * vec2[1], vec1[2] * vec2[0] - vec1[0] * vec2[2], vec1[0] * vec2[1] - vec1[1] * vec2[0], ]; }, normalizeVec(vec) { const norm = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); return [vec[0] / norm, vec[1] / norm, vec[2] / norm]; }, getEmptyGeom() { return { points: [], faces: [], normals: [], uvs: [], uvs2: [], offset: 0, } }, }; module.exports = geomHelper;