From 3b575c149da0fba044892cd245ab9565c705c885 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Wed, 7 Feb 2018 00:45:01 -0800 Subject: [PATCH] fix a bug when two merge faces make a cut and create a new curve/edge forbid partially tangent edges with new curve - should be handled level before update tests --- web/app/brep/operations/boolean.js | 79 ++++++++++++++++++------------ web/test/cases/brep-bool-smoke.js | 4 +- web/test/cases/brep-bool-topo.js | 20 ++++++++ 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index d37f7132..fef7caf4 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -168,12 +168,23 @@ function replaceEdges() { } function replaceMergedFaces(facesData, mergedFaces) { + function addDecayed(he, out) { + let decayed = EdgeSolveData.get(he).decayed; + if (decayed) { + decayed.forEach(de => addDecayed(de, out)); + } else { + out.push(he); + } + } filterInPlace(facesData, ({face}) => mergedFaces.find(({originFaces}) => originFaces.indexOf(face) > -1) === undefined ); for (let {mergedLoops, referenceSurface, originFaces} of mergedFaces) { let fakeFace = new Face(referenceSurface); for (let mergedLoop of mergedLoops) { + let actualHalfEdges = []; + mergedLoop.halfEdges.forEach(he => addDecayed(he, actualHalfEdges)); + mergedLoop.halfEdges = actualHalfEdges; fakeFace.innerLoops.push(mergedLoop); mergedLoop.face = fakeFace; mergedLoop.link(); @@ -654,6 +665,10 @@ function intersectFaces(shellA, shellB, operationType) { __DEBUG__.AddCurve(curve); } + if (hasCoincidentEdge(curve, faceA) || hasCoincidentEdge(curve, faceB)) { + continue; + } + curve = fixCurveDirection(curve, faceA.surface, faceB.surface, operationType); const nodes = []; collectNodesOfIntersectionOfFace(curve, faceA, nodes, A); @@ -740,6 +755,15 @@ function nodeByPoint(nodes, point, u, curve, vertex) { return node; } +function hasCoincidentEdge(curve, face) { + for (let edge of face.edges) { + if (curveAndEdgeCoincident(curve, edge)) { + return true; + } + } + return false; +} + function collectNodesOfIntersectionOfFace(curve, face, nodes, operand) { for (let loop of face.loops) { collectNodesOfIntersection(curve, loop, nodes, operand); @@ -749,27 +773,9 @@ function collectNodesOfIntersectionOfFace(curve, face, nodes, operand) { function collectNodesOfIntersection(curve, loop, nodes, operand) { // __DEBUG__.AddCurve(curve, 0xffffff); let skippedEnclosures = new Set(); - let coincidentEdges = new Set(); - for (let edge of loop.halfEdges) { - if (curveAndEdgeCoincident(curve, edge)) { - coincidentEdges.add(edge); - } - } let encloses = loop.encloses; for (let [a, b, v] of encloses) { - if (coincidentEdges.has(a)) { - let sameDir = a.tangentAtStart().dot(curve.tangentAtPoint(a.vertexA.point)) > 0; - let vertex = sameDir ? a.vertexA : a.vertexB; - skippedEnclosures.add(vertex); - let node = nodeByPoint(nodes, vertex.point, undefined, curve, vertex); - node.leaves[operand] = true; - } - } - for (let [a, b, v] of encloses) { - if (coincidentEdges.has(a) && coincidentEdges.has(b)) { - continue; - } if (skippedEnclosures.has(v)) { continue; } @@ -786,9 +792,7 @@ function collectNodesOfIntersection(curve, loop, nodes, operand) { } } for (let edge of loop.halfEdges) { - if (!coincidentEdges.has(edge)) { - intersectCurveWithEdge(curve, edge, nodes, operand); - } + intersectCurveWithEdge(curve, edge, nodes, operand); } } @@ -909,16 +913,29 @@ function splitEdgeByVertex(edge, vertex) { h2.next = halfEdge.next; h2.next.prev = h2; + EdgeSolveData.createIfEmpty(halfEdge).decayed = [h1, h2]; } updateInLoop(edge.halfEdge1, edge1.halfEdge1, edge2.halfEdge1); updateInLoop(edge.halfEdge2, edge2.halfEdge2, edge1.halfEdge2); - EdgeSolveData.transfer(edge.halfEdge1, edge1.halfEdge1); - EdgeSolveData.transfer(edge.halfEdge1, edge2.halfEdge1); + function transferPriority(from, to) { + let priority = getPriority(from); + if (priority !== 0) { + EdgeSolveData.setPriority(to, priority); + } + } - EdgeSolveData.transfer(edge.halfEdge2, edge2.halfEdge2); - EdgeSolveData.transfer(edge.halfEdge2, edge1.halfEdge2); + transferPriority(edge.halfEdge1, edge1.halfEdge1); + transferPriority(edge.halfEdge1, edge2.halfEdge1); + transferPriority(edge.halfEdge2, edge2.halfEdge2); + transferPriority(edge.halfEdge2, edge1.halfEdge2); + + if (isEdgeTransferred(edge)) { + markEdgeTransferred(edge1); + markEdgeTransferred(edge2); + } + return [edge1, edge2]; } @@ -1026,10 +1043,6 @@ EdgeSolveData.clear = function(edge) { delete edge.data[MY]; }; -EdgeSolveData.transfer = function(from, to) { - to.data[MY] = from.data[MY]; -}; - EdgeSolveData.setPriority = function(halfEdge, value) { EdgeSolveData.createIfEmpty(halfEdge).priority = value; }; @@ -1232,13 +1245,19 @@ function isSameEdge(e1, e2) { function curveAndEdgeCoincident(curve, edge) { let tess = edge.tessellate(); - //Do reverese to optimaze a bit because the first point is usually checked + //Do reverse to optimize a bit because the first point is usually checked + let touches = 0; for (let i = tess.length - 1; i >= 0; i--) { let pt1 = tess[i]; let pt2 = curve.point(curve.param(pt1)); if (!veq(pt1, pt2)) { + if (touches > 1) { + //partial tangency should be handled before face-face intersection analysis + throw new CadError('BOOLEAN_INVALID_RESULT', {edge}); + } return false; } + touches++; } return true; } diff --git a/web/test/cases/brep-bool-smoke.js b/web/test/cases/brep-bool-smoke.js index 585ee255..f1ade652 100644 --- a/web/test/cases/brep-bool-smoke.js +++ b/web/test/cases/brep-bool-smoke.js @@ -20,7 +20,7 @@ export default defineTests([ 'expected': { 'format': 'LOOPS', 'vertices': [[-293, 148, 0], [-293, 148, 112], [-159, 233, 62], [-159, 75, 62], [-159, 75, 112], [-159, 233, 112], [-115, 261, 112], [-115, 261, 62], [-110, 48, 112], [-110, 48, 62], [-38, 9, 0], [-38, 9, 112], [-12, 326, 0], [-12, 326, 112], [38, 48, 62], [38, 48, 112], [38, 261, 62], [38, 261, 112], [332, 110, 0], [332, 110, 112]], - 'faces': [[[19, 18, 12, 13]], [[11, 10, 18, 19]], [[9, 3, 4, 1, 0, 10, 11, 8]], [[2, 7, 6, 13, 12, 0, 1, 5]], [[0, 12, 18, 10]], [[15, 17, 16, 14]], [[6, 7, 16, 17]], [[2, 5, 4, 3]], [[9, 8, 15, 14]], [[7, 2, 3, 9, 14, 16]], [[1, 4, 5]], [[6, 17, 15, 8, 11, 19, 13]]] + 'faces': [[[19, 18, 12, 13]], [[11, 10, 18, 19]], [[9, 3, 4, 1, 0, 10, 11, 8]], [[2, 7, 6, 13, 12, 0, 1, 5]], [[0, 12, 18, 10]], [[15, 17, 16, 14]], [[6, 7, 16, 17]], [[2, 5, 4, 3]], [[9, 8, 15, 14]], [[7, 2, 3, 9, 14, 16]], [[4, 5, 1]], [[6, 17, 15, 8, 11, 19, 13]]] } }, { @@ -54,7 +54,7 @@ export default defineTests([ 'expected': { 'format': 'LOOPS', 'vertices': [[-300, 13, 17], [-300, 13, 250], [-300, 250, 17], [-300, 250, 250], [-250, -250, -250], [-250, -250, 250], [-250, 13, 17], [-250, 13, 250], [-250, 250, -250], [-250, 250, 17], [-250, 250, 250], [-120, -250, -49], [-120, -250, 172], [-120, 250, -49], [-120, 250, 172], [-107, -155, 250], [-107, -155, 300], [-107, 53, 250], [-107, 53, 300], [-82, 200, -160], [-82, 200, -49], [-82, 200, 172], [-82, 200, 210], [-82, 250, -160], [-82, 250, -49], [-82, 250, 172], [-82, 250, 210], [-60, 200, 172], [-60, -73, -250], [-60, -73, -49], [-60, -73, 172], [-60, -73, 300], [-60, 53, 250], [-60, 53, 300], [-60, 200, -160], [-60, 200, 210], [-60, 212, -250], [-60, 212, -160], [-60, 212, 210], [-60, 212, 250], [-60, 200, -49], [70, 212, -160], [70, 212, 172], [70, 212, 210], [70, 250, -160], [70, 250, -49], [70, 250, 172], [70, 250, 210], [70, 212, -49], [107, -250, -49], [107, -250, 172], [107, -73, 172], [107, -73, -49], [107, 212, -49], [107, 212, 172], [107, 250, -49], [107, 250, 172], [140, -155, 250], [140, -155, 300], [140, -73, 250], [140, -73, 300], [220, -73, -250], [220, -73, 250], [220, 42, -155], [220, 42, -44], [220, 188, -155], [220, 188, -44], [220, 212, -250], [220, 212, 250], [250, -250, -250], [250, -250, 250], [250, 42, -155], [250, 42, -44], [250, 188, -155], [250, 188, -44], [250, 250, -250], [250, 250, 250]], - 'faces': [[[1, 0, 6, 7]], [[0, 2, 9, 6]], [[0, 1, 3, 2]], [[6, 9, 8, 4, 5, 7]], [[17, 18, 33, 32]], [[58, 57, 59, 60]], [[16, 15, 57, 58]], [[18, 17, 15, 16]], [[58, 60, 31, 33, 18, 16]], [[1, 7, 5, 70, 76, 10, 3], [17, 32, 39, 68, 62, 59, 57, 15]], [[56, 55, 53, 54]], [[49, 50, 51, 52]], [[21, 27, 30, 51, 50, 12, 14, 25]], [[42, 46, 56, 54]], [[11, 13, 14, 12]], [[40, 20, 24, 13, 11, 49, 52, 29]], [[45, 48, 53, 55]], [[5, 4, 69, 70], [50, 49, 11, 12]], [[67, 61, 62, 68], [63, 65, 66, 64]], [[61, 28, 29, 52, 51, 30, 31, 60, 59, 62]], [[34, 40, 29, 28, 36, 37]], [[27, 35, 38, 39, 32, 33, 31, 30]], [[38, 43, 42, 54, 53, 48, 41, 37, 36, 67, 68, 39]], [[8, 75, 69, 4], [67, 36, 28, 61]], [[72, 64, 66, 74]], [[71, 63, 64, 72]], [[73, 65, 63, 71]], [[74, 66, 65, 73]], [[70, 69, 75, 76], [73, 71, 72, 74]], [[41, 48, 45, 44]], [[42, 43, 47, 46]], [[37, 41, 44, 23, 19, 34]], [[24, 20, 19, 23]], [[21, 25, 26, 22]], [[43, 38, 35, 22, 26, 47]], [[35, 27, 21, 22]], [[40, 34, 19, 20]], [[2, 3, 10, 76, 75, 8, 9], [56, 46, 47, 26, 25, 14, 13, 24, 23, 44, 45, 55]]] + 'faces': [[[1, 0, 6, 7]], [[0, 2, 9, 6]], [[0, 1, 3, 2]], [[6, 9, 8, 4, 5, 7]], [[17, 18, 33, 32]], [[58, 57, 59, 60]], [[16, 15, 57, 58]], [[18, 17, 15, 16]], [[58, 60, 31, 33, 18, 16]], [[5, 70, 76, 10, 3, 1, 7], [17, 32, 39, 68, 62, 59, 57, 15]], [[56, 55, 53, 54]], [[49, 50, 51, 52]], [[21, 27, 30, 51, 50, 12, 14, 25]], [[42, 46, 56, 54]], [[11, 13, 14, 12]], [[40, 20, 24, 13, 11, 49, 52, 29]], [[45, 48, 53, 55]], [[5, 4, 69, 70], [50, 49, 11, 12]], [[67, 61, 62, 68], [63, 65, 66, 64]], [[61, 28, 29, 52, 51, 30, 31, 60, 59, 62]], [[34, 40, 29, 28, 36, 37]], [[27, 35, 38, 39, 32, 33, 31, 30]], [[38, 43, 42, 54, 53, 48, 41, 37, 36, 67, 68, 39]], [[8, 75, 69, 4], [67, 36, 28, 61]], [[72, 64, 66, 74]], [[71, 63, 64, 72]], [[73, 65, 63, 71]], [[74, 66, 65, 73]], [[70, 69, 75, 76], [73, 71, 72, 74]], [[41, 48, 45, 44]], [[42, 43, 47, 46]], [[37, 41, 44, 23, 19, 34]], [[24, 20, 19, 23]], [[21, 25, 26, 22]], [[43, 38, 35, 22, 26, 47]], [[35, 27, 21, 22]], [[40, 34, 19, 20]], [[9, 2, 3, 10, 76, 75, 8], [46, 47, 26, 25, 14, 13, 24, 23, 44, 45, 55, 56]]] } } diff --git a/web/test/cases/brep-bool-topo.js b/web/test/cases/brep-bool-topo.js index 40a0e898..a552fa03 100644 --- a/web/test/cases/brep-bool-topo.js +++ b/web/test/cases/brep-bool-topo.js @@ -98,6 +98,26 @@ export default defineTests([ 'vertices': [[-250, -250, -250], [-250, -250, 250], [-250, -1, -250], [-250, -1, 91], [-250, 250, 91], [-250, 250, 250], [-200, -1, -250], [-200, -1, 91], [-200, 250, -250], [-200, 250, 91], [250, -250, -250], [250, -250, 250], [250, 250, -250], [250, 250, 250]], 'faces': [[[10, 11, 1, 0]], [[12, 13, 11, 10]], [[5, 1, 11, 13]], [[6, 8, 12, 10, 0, 2]], [[6, 2, 3, 7]], [[3, 4, 9, 7]], [[8, 6, 7, 9]], [[8, 9, 4, 5, 13, 12]], [[4, 3, 2, 0, 1, 5]]] } + }, + + { + 'name': 'MergedFacesCreateIntersectionCurve', + 'state': { + 'sketches': { + '1:5': {'Segment': [[[92, 144], [351, 144]], [[351, 144], [351, -106]], [[351, -106], [92, -106]], [[92, -106], [92, 144]]]}, + '0:2': {'Segment': [[[-172, 145], [30, 145]], [[30, 145], [30, -99]], [[30, -99], [-172, -99]], [[-172, -99], [-172, 145]]]} + }, + 'operations': [{'type': 'BOX', 'params': {'width': 500, 'height': 500, 'depth': 500}}, { + 'type': 'CUT', + 'params': {'value': 50, 'prism': 1, 'angle': 0, 'rotation': 0, 'face': '0:2'} + }, {'type': 'CUT', 'params': {'value': 50, 'prism': 1, 'angle': 0, 'rotation': 0, 'face': '1:5'}}] + }, + 'expected': { + 'format': 'LOOPS', + 'vertices': [[-250, -250, -250], [-250, -250, 250], [-250, 200, -106], [-250, 200, 144], [-250, 250, -250], [-250, 250, -106], [-250, 250, 144], [-250, 250, 250], [-92, 200, -106], [-92, 200, 144], [-92, 250, -106], [-92, 250, 144], [-30, 200, -99], [-30, 200, 145], [-30, 250, -99], [-30, 250, 145], [172, 200, -99], [172, 200, 145], [172, 250, -99], [172, 250, 145], [250, -250, -250], [250, -250, 250], [250, 250, -250], [250, 250, 250]], + 'faces': [[[0, 20, 21, 1]], [[20, 22, 23, 21]], [[3, 2, 5, 4, 0, 1, 7, 6]], [[23, 7, 1, 21]], [[22, 20, 0, 4]], [[17, 19, 18, 16]], [[16, 18, 14, 12]], [[12, 14, 15, 13]], [[13, 15, 19, 17]], [[11, 10, 8, 9]], [[5, 2, 8, 10]], [[3, 6, 11, 9]], [[2, 3, 9, 8]], [[16, 12, 13, 17]], [[10, 11, 6, 7, 23, 22, 4, 5], [15, 14, 18, 19]]] + } } + ]);