diff --git a/web/app/3d/modeler-app.js b/web/app/3d/modeler-app.js index 986a7362..1236905b 100644 --- a/web/app/3d/modeler-app.js +++ b/web/app/3d/modeler-app.js @@ -91,7 +91,7 @@ App.prototype.BREPTestImpl = function() { //addToScene(box1); //addToScene(box2); - const result = BREPBool.union(box1, box2); + const result = BREPBool.subtract(box1, box2); addToScene(result); this.viewer.render() diff --git a/web/app/brep/brep-builder.js b/web/app/brep/brep-builder.js index 7b28a622..f8baa3b2 100644 --- a/web/app/brep/brep-builder.js +++ b/web/app/brep/brep-builder.js @@ -59,6 +59,7 @@ export function createPrism(basePoints, height) { lidFace.debugName = 'lid'; shell.faces.push(baseFace, lidFace); + shell.faces.forEach(f => f.shell = shell); return shell; } diff --git a/web/app/brep/brep-validator.js b/web/app/brep/brep-validator.js index 9c38695a..3671b6b9 100644 --- a/web/app/brep/brep-validator.js +++ b/web/app/brep/brep-validator.js @@ -9,6 +9,9 @@ export class BREPValidator { validateShell(shell) { for (let face of shell.faces) { + if (face.shell !== shell) { + this.addError(new FaceRefersToWrongShell(face, shell)) + } this.validateFace(face); } } @@ -77,6 +80,17 @@ BREPValidator.validateToConsole = function(shell) { } }; +class FaceRefersToWrongShell { + constructor(face, shell) { + this.face = face; + this.shell = shell; + } + + message() { + return "face refers to a shell it doesn't belong to"; + } +} + class VerticesOfHalfEdgeArentConnected { constructor(loop, halfEdge1, halfEdge2) { this.loop = loop; diff --git a/web/app/brep/geom/impl/plane.js b/web/app/brep/geom/impl/plane.js index 4d614aa7..4a814dbe 100644 --- a/web/app/brep/geom/impl/plane.js +++ b/web/app/brep/geom/impl/plane.js @@ -30,4 +30,7 @@ export class Plane extends Surface { return super.intersect(); } + invert() { + return new Plane(this.normal.multiply(-1), - this.w); + } } \ No newline at end of file diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index a9f1704e..c6cdd1ee 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -12,22 +12,62 @@ import {Matrix3} from '../../math/l3space'; export const TOLERANCE = 1e-8; +const TYPE = { + UNION: 0, + INTERSECT: 1, + SUBTRACT: 2 +}; + export function union( shell1, shell2 ) { + return BooleanAlgorithm(shell1, shell2, TYPE.UNION); +} + +export function intersect( shell1, shell2 ) { + return BooleanAlgorithm(shell1, shell2, TYPE.INTERSECT); +} + +export function subtract( shell1, shell2 ) { + invert(shell2); + return BooleanAlgorithm(shell1, shell2, TYPE.SUBTRACT); +} + +export function invert( shell ) { + for (let face of shell.faces) { + face.surface = face.surface.invert(); + invertLoop(face.outerLoop); + } + BREPValidator.validateToConsole(shell); +} + +function invertLoop(loop) { + for (let halfEdge of loop.halfEdges) { + const t = halfEdge.vertexA; + halfEdge.vertexA = halfEdge.vertexB; + halfEdge.vertexB = t; + } + loop.halfEdges.reverse(); + BREPBuilder.linkSegments(loop.halfEdges); +} + +export function BooleanAlgorithm( shell1, shell2, type ) { const facesData = []; initSolveData(shell1, facesData); initSolveData(shell2, facesData); - intersectFaces(shell1, shell2); + intersectFaces(shell1, shell2, type !== TYPE.UNION); const result = new Shell(); //__DEBUG__.AddSegment(shell2.faces[0].outerLoop.halfEdges[0].vertexA.point, shell2.faces[0].outerLoop.halfEdges[0].vertexB.point) for (let faceData of facesData) { - - const seen = new Set(); const face = faceData.face; + if (faceData.newEdges.length == 0) { + if (type === TYPE.INTERSECT) continue; + if (type === TYPE.SUBTRACT && face.shell == shell2) continue; + } + const seen = new Set(); const edges = face.outerLoop.halfEdges.concat(faceData.newEdges); while (true) { let edge = edges.pop(); @@ -61,6 +101,7 @@ export function union( shell1, shell2 ) { const newFace = new Face(face.surface); newFace.outerLoop = loop; newFace.outerLoop.face = newFace; + newFace.shell = result; result.faces.push(newFace); } } @@ -101,7 +142,7 @@ function findMaxTurningLeft(edge, edges) { return edges[0]; } -function intersectFaces(shell1, shell2) { +function intersectFaces(shell1, shell2, inverseCrossEdgeDirection) { for (let i = 0; i < shell1.faces.length; i++) { for (let j = 0; j < shell2.faces.length; j++) { const face1 = shell1.faces[i]; @@ -115,11 +156,13 @@ function intersectFaces(shell1, shell2) { const newEdges = []; const direction = face1.surface.normal.cross(face2.surface.normal); + if (inverseCrossEdgeDirection) { + direction._multiply(-1); + } split(nodes, newEdges, curve, direction); newEdges.forEach(e => { //__DEBUG__.AddHalfEdge(e.halfEdge1); - console.log("new edge"); face1.__faceSolveData.newEdges.push(e.halfEdge1); face2.__faceSolveData.newEdges.push(e.halfEdge2); diff --git a/web/app/brep/topo/face.js b/web/app/brep/topo/face.js index db4a6079..d77dde3c 100644 --- a/web/app/brep/topo/face.js +++ b/web/app/brep/topo/face.js @@ -3,6 +3,7 @@ export class Face { constructor(surface) { this.surface = surface; + this.shell = null; this.outerLoop = null; this.innerLoops = []; }