UNPKG

@jscad/modeling

Version:

Constructive Solid Geometry (CSG) Library for JSCAD

94 lines (84 loc) 3.63 kB
const geom3 = require('../../geometries/geom3') const poly3 = require('../../geometries/poly3') const { NEPS } = require('../../maths/constants') const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons') /* After boolean operations all coplanar polygon fragments are joined by a retesselating operation. geom3.reTesselate(geom). Retesselation is done through a linear sweep over the polygon surface. The sweep line passes over the y coordinates of all vertices in the polygon. Polygons are split at each sweep line, and the fragments are joined horizontally and vertically into larger polygons (making sure that we will end up with convex polygons). */ const retessellate = (geometry) => { if (geometry.isRetesselated) { return geometry } const polygons = geom3.toPolygons(geometry).map((polygon, index) => ({ vertices: polygon.vertices, plane: poly3.plane(polygon), index: index })) const classified = classifyPolygons(polygons) const destPolygons = [] classified.forEach((group) => { if (Array.isArray(group)) { const reTessellateCoplanarPolygons = reTesselateCoplanarPolygons(group) destPolygons.push(...reTessellateCoplanarPolygons) } else { destPolygons.push(group) } }) const result = geom3.create(destPolygons) result.isRetesselated = true return result } const classifyPolygons = (polygons) => { let clusters = [polygons] // a cluster is an array of potentially coplanar polygons const nonCoplanar = [] // polygons that are known to be non-coplanar // go through each component of the plane starting with the last one (the distance from origin) for (let component = 3; component >= 0; component--) { const maybeCoplanar = [] const tolerance = component === 3 ? 0.000000015 : NEPS clusters.forEach((cluster) => { // sort the cluster by the current component cluster.sort(byPlaneComponent(component, tolerance)) // iterate through the cluster and check if there are polygons which are not coplanar with the others // or if there are sub-clusters of coplanar polygons let startIndex = 0 for (let i = 1; i < cluster.length; i++) { // if there's a difference larger than the tolerance, split the cluster if (cluster[i].plane[component] - cluster[startIndex].plane[component] > tolerance) { // if there's a single polygon it's definitely not coplanar with any others if (i - startIndex === 1) { nonCoplanar.push(cluster[startIndex]) } else { // we have a new sub cluster of potentially coplanar polygons maybeCoplanar.push(cluster.slice(startIndex, i)) } startIndex = i } } // handle the last elements of the cluster if (cluster.length - startIndex === 1) { nonCoplanar.push(cluster[startIndex]) } else { maybeCoplanar.push(cluster.slice(startIndex)) } }) // replace previous clusters with the new ones clusters = maybeCoplanar } // restore the original order of the polygons const result = [] // polygons inside the cluster should already be sorted by index clusters.forEach((cluster) => { if (cluster[0]) result[cluster[0].index] = cluster }) nonCoplanar.forEach((polygon) => { result[polygon.index] = polygon }) return result } const byPlaneComponent = (component, tolerance) => (a, b) => { if (a.plane[component] - b.plane[component] > tolerance) { return 1 } else if (b.plane[component] - a.plane[component] > tolerance) { return -1 } return 0 } module.exports = retessellate