1 // Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #include <GEOMImpl_BooleanDriver.hxx>
24 #include <GEOMImpl_IBoolean.hxx>
25 #include <GEOMImpl_Types.hxx>
26 #include <GEOMImpl_GlueDriver.hxx>
27 #include <GEOM_Function.hxx>
28 #include <GEOMUtils.hxx>
30 #include <TNaming_CopyShape.hxx>
32 #include <ShapeFix_ShapeTolerance.hxx>
33 #include <ShapeFix_Shape.hxx>
35 #include <BRep_Builder.hxx>
36 #include <BRepAlgo.hxx>
37 #include <BRepAlgoAPI_Common.hxx>
38 #include <BRepAlgoAPI_Cut.hxx>
39 #include <BRepAlgoAPI_Fuse.hxx>
40 #include <BRepAlgoAPI_Section.hxx>
41 #include <BRepCheck_Analyzer.hxx>
43 #include <TopExp_Explorer.hxx>
44 #include <TopoDS_Shape.hxx>
45 #include <TopoDS_Compound.hxx>
46 #include <TopoDS_Iterator.hxx>
47 #include <TopTools_MapOfShape.hxx>
48 #include <TopTools_ListOfShape.hxx>
49 #include <TopTools_ListIteratorOfListOfShape.hxx>
51 #include <TColStd_IndexedDataMapOfTransientTransient.hxx>
53 #include <Precision.hxx>
55 #include <Standard_ConstructionError.hxx>
56 #include <StdFail_NotDone.hxx>
58 //=======================================================================
61 //=======================================================================
62 const Standard_GUID& GEOMImpl_BooleanDriver::GetID()
64 static Standard_GUID aBooleanDriver("FF1BBB21-5D14-4df2-980B-3A668264EA16");
65 return aBooleanDriver;
68 //=======================================================================
69 //function : GEOMImpl_BooleanDriver
71 //=======================================================================
72 GEOMImpl_BooleanDriver::GEOMImpl_BooleanDriver()
76 //=======================================================================
79 //=======================================================================
80 Standard_Integer GEOMImpl_BooleanDriver::Execute (TFunction_Logbook& log) const
82 if (Label().IsNull()) return 0;
83 Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
85 GEOMImpl_IBoolean aCI (aFunction);
86 Standard_Integer aType = aFunction->GetType();
90 Handle(GEOM_Function) aRefShape1 = aCI.GetShape1();
91 Handle(GEOM_Function) aRefShape2 = aCI.GetShape2();
92 TopoDS_Shape aShape1 = aRefShape1->GetValue();
93 TopoDS_Shape aShape2 = aRefShape2->GetValue();
95 if (!aShape1.IsNull() && !aShape2.IsNull()) {
96 // check arguments for Mantis issue 0021019
97 BRepCheck_Analyzer ana (aShape1, Standard_True);
99 StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
102 StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
104 // perform COMMON operation
105 if (aType == BOOLEAN_COMMON) {
110 TopTools_ListOfShape listShape1, listShape2;
111 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
112 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
114 Standard_Boolean isCompound =
115 (listShape1.Extent() > 1 || listShape2.Extent() > 1);
117 TopTools_ListIteratorOfListOfShape itSub1 (listShape1);
118 for (; itSub1.More(); itSub1.Next()) {
119 TopoDS_Shape aValue1 = itSub1.Value();
120 TopTools_ListIteratorOfListOfShape itSub2 (listShape2);
121 for (; itSub2.More(); itSub2.Next()) {
122 TopoDS_Shape aValue2 = itSub2.Value();
123 BRepAlgoAPI_Common BO (aValue1, aValue2);
125 StdFail_NotDone::Raise("Common operation can not be performed on the given shapes");
128 TopoDS_Shape aStepResult = BO.Shape();
130 // check result of this step: if it is a compound (boolean operations
131 // allways return a compound), we add all sub-shapes of it.
132 // This allows to avoid adding empty compounds,
133 // resulting from COMMON on two non-intersecting shapes.
134 if (aStepResult.ShapeType() == TopAbs_COMPOUND) {
135 TopoDS_Iterator aCompIter (aStepResult);
136 for (; aCompIter.More(); aCompIter.Next()) {
137 // add shape in a result
138 B.Add(C, aCompIter.Value());
142 // add shape in a result
143 B.Add(C, aStepResult);
153 TopTools_ListOfShape listShapeC;
154 GEOMUtils::AddSimpleShapes(C, listShapeC);
155 TopTools_ListIteratorOfListOfShape itSubC (listShapeC);
156 bool isOnlySolids = true;
157 for (; itSubC.More(); itSubC.Next()) {
158 TopoDS_Shape aValueC = itSubC.Value();
159 if (aValueC.ShapeType() != TopAbs_SOLID) isOnlySolids = false;
162 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
167 // As GlueFaces has been improved to keep all kind of shapes
168 TopExp_Explorer anExp (C, TopAbs_VERTEX);
170 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
176 // perform CUT operation
177 else if (aType == BOOLEAN_CUT) {
182 TopTools_ListOfShape listShapes, listTools;
183 GEOMUtils::AddSimpleShapes(aShape1, listShapes);
184 GEOMUtils::AddSimpleShapes(aShape2, listTools);
186 Standard_Boolean isCompound = (listShapes.Extent() > 1);
188 TopTools_ListIteratorOfListOfShape itSub1 (listShapes);
189 for (; itSub1.More(); itSub1.Next()) {
190 TopoDS_Shape aCut = itSub1.Value();
192 TopTools_ListIteratorOfListOfShape itSub2 (listTools);
193 for (; itSub2.More(); itSub2.Next()) {
194 TopoDS_Shape aTool = itSub2.Value();
195 BRepAlgoAPI_Cut BO (aCut, aTool);
197 StdFail_NotDone::Raise("Cut operation can not be performed on the given shapes");
202 // check result of this step: if it is a compound (boolean operations
203 // allways return a compound), we add all sub-shapes of it.
204 // This allows to avoid adding empty compounds,
205 // resulting from CUT of parts
206 if (aCut.ShapeType() == TopAbs_COMPOUND) {
207 TopoDS_Iterator aCompIter (aCut);
208 for (; aCompIter.More(); aCompIter.Next()) {
209 // add shape in a result
210 B.Add(C, aCompIter.Value());
214 // add shape in a result
224 TopTools_ListOfShape listShapeC;
225 GEOMUtils::AddSimpleShapes(C, listShapeC);
226 TopTools_ListIteratorOfListOfShape itSubC (listShapeC);
227 bool isOnlySolids = true;
228 for (; itSubC.More(); itSubC.Next()) {
229 TopoDS_Shape aValueC = itSubC.Value();
230 if (aValueC.ShapeType() != TopAbs_SOLID) isOnlySolids = false;
233 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
238 // As GlueFaces has been improved to keep all kind of shapes
239 TopExp_Explorer anExp (C, TopAbs_VERTEX);
241 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
247 // perform FUSE operation
248 else if (aType == BOOLEAN_FUSE) {
249 /* Fix for NPAL15379: refused
251 TopTools_ListOfShape listShape1, listShape2;
252 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
253 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
255 Standard_Boolean isIntersect = Standard_False;
257 if (listShape1.Extent() > 1 && !isIntersect) {
258 // check intersections inside the first compound
259 TopTools_ListIteratorOfListOfShape it1 (listShape1);
260 for (; it1.More() && !isIntersect; it1.Next()) {
261 TopoDS_Shape aValue1 = it1.Value();
262 TopTools_ListIteratorOfListOfShape it2 (listShape1);
263 for (; it2.More() && !isIntersect; it2.Next()) {
264 TopoDS_Shape aValue2 = it2.Value();
265 if (aValue2 != aValue1) {
266 BRepAlgoAPI_Section BO (aValue1, aValue2);
268 TopoDS_Shape aSect = BO.Shape();
269 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
271 isIntersect = Standard_True;
279 if (listShape2.Extent() > 1 && !isIntersect) {
280 // check intersections inside the second compound
281 TopTools_ListIteratorOfListOfShape it1 (listShape2);
282 for (; it1.More() && !isIntersect; it1.Next()) {
283 TopoDS_Shape aValue1 = it1.Value();
284 TopTools_ListIteratorOfListOfShape it2 (listShape2);
285 for (; it2.More() && !isIntersect; it2.Next()) {
286 TopoDS_Shape aValue2 = it2.Value();
287 if (aValue2 != aValue1) {
288 BRepAlgoAPI_Section BO (aValue1, aValue2);
290 TopoDS_Shape aSect = BO.Shape();
291 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
293 isIntersect = Standard_True;
302 // have intersections inside compounds
303 // check intersections between compounds
304 TopTools_ListIteratorOfListOfShape it1 (listShape1);
305 for (; it1.More(); it1.Next()) {
306 TopoDS_Shape aValue1 = it1.Value();
307 TopTools_ListIteratorOfListOfShape it2 (listShape2);
308 for (; it2.More(); it2.Next()) {
309 TopoDS_Shape aValue2 = it2.Value();
310 if (aValue2 != aValue1) {
311 BRepAlgoAPI_Section BO (aValue1, aValue2);
313 TopoDS_Shape aSect = BO.Shape();
314 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
316 StdFail_NotDone::Raise("Bad argument for Fuse: compound with intersecting sub-shapes");
326 BRepAlgoAPI_Fuse BO (aShape1, aShape2);
328 StdFail_NotDone::Raise("Fuse operation can not be performed on the given shapes");
333 // perform SECTION operation
334 else if (aType == BOOLEAN_SECTION) {
339 TopTools_ListOfShape listShape1, listShape2;
340 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
341 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
343 Standard_Boolean isCompound =
344 (listShape1.Extent() > 1 || listShape2.Extent() > 1);
346 TopTools_ListIteratorOfListOfShape itSub1 (listShape1);
347 for (; itSub1.More(); itSub1.Next()) {
348 TopoDS_Shape aValue1 = itSub1.Value();
349 TopTools_ListIteratorOfListOfShape itSub2 (listShape2);
350 for (; itSub2.More(); itSub2.Next()) {
351 TopoDS_Shape aValue2 = itSub2.Value();
352 BRepAlgoAPI_Section BO (aValue1, aValue2, Standard_False);
353 // Set approximation to have an attached 3D BSpline geometry to each edge,
354 // where analytic curve is not possible. Without this flag in some cases
355 // we obtain BSpline curve of degree 1 (C0), which is slowly
356 // processed by some algorithms (Partition for example).
357 BO.Approximation(Standard_True);
358 //modified by NIZNHY-PKV Tue Oct 18 14:34:16 2011f
359 BO.ComputePCurveOn1(Standard_True);
360 BO.ComputePCurveOn2(Standard_True);
361 //modified by NIZNHY-PKV Tue Oct 18 14:34:18 2011t
365 StdFail_NotDone::Raise("Section operation can not be performed on the given shapes");
368 TopoDS_Shape aStepResult = BO.Shape();
370 // check result of this step: if it is a compound (boolean operations
371 // allways return a compound), we add all sub-shapes of it.
372 // This allows to avoid adding empty compounds,
373 // resulting from SECTION on two non-intersecting shapes.
374 if (aStepResult.ShapeType() == TopAbs_COMPOUND) {
375 TopoDS_Iterator aCompIter (aStepResult);
376 for (; aCompIter.More(); aCompIter.Next()) {
377 // add shape in a result
378 B.Add(C, aCompIter.Value());
382 // add shape in a result
383 B.Add(C, aStepResult);
394 // As GlueFaces has been improved to keep all kind of shapes
395 TopExp_Explorer anExp (C, TopAbs_VERTEX);
397 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
408 if (aShape.IsNull()) return 0;
410 // as boolean operations always produce compound, lets simplify it
411 // for the case, if it contains only one sub-shape
412 TopTools_ListOfShape listShapeRes;
413 GEOMUtils::AddSimpleShapes(aShape, listShapeRes);
414 if (listShapeRes.Extent() == 1) {
415 aShape = listShapeRes.First();
416 if (aShape.IsNull()) return 0;
419 // 08.07.2008 skl for bug 19761 from Mantis
420 BRepCheck_Analyzer ana (aShape, Standard_True);
422 if (!ana.IsValid()) {
423 ShapeFix_ShapeTolerance aSFT;
424 aSFT.LimitTolerance(aShape, Precision::Confusion(),
425 Precision::Confusion(), TopAbs_SHAPE);
426 Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape(aShape);
428 aShape = aSfs->Shape();
431 Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
433 //if (!BRepAlgo::IsValid(aShape)) {
434 // Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
437 // BEGIN: Mantis issue 0021060: always limit tolerance of BOP result
438 // 1. Get shape parameters for comparison
439 int nbTypes [TopAbs_SHAPE];
441 for (int iType = 0; iType < TopAbs_SHAPE; ++iType)
443 nbTypes[aShape.ShapeType()]++;
445 TopTools_MapOfShape aMapOfShape;
446 aMapOfShape.Add(aShape);
447 TopTools_ListOfShape aListOfShape;
448 aListOfShape.Append(aShape);
450 TopTools_ListIteratorOfListOfShape itL (aListOfShape);
451 for (; itL.More(); itL.Next()) {
452 TopoDS_Iterator it (itL.Value());
453 for (; it.More(); it.Next()) {
454 TopoDS_Shape s = it.Value();
455 if (aMapOfShape.Add(s)) {
456 aListOfShape.Append(s);
457 nbTypes[s.ShapeType()]++;
463 // 2. Limit tolerance
464 TopoDS_Shape aShapeCopy;
465 TColStd_IndexedDataMapOfTransientTransient aMapTShapes;
466 TNaming_CopyShape::CopyTool(aShape, aMapTShapes, aShapeCopy);
467 ShapeFix_ShapeTolerance aSFT;
468 aSFT.LimitTolerance(aShapeCopy, Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE);
469 Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape (aShapeCopy);
471 aShapeCopy = aSfs->Shape();
473 // 3. Check parameters
474 ana.Init(aShapeCopy);
476 int iType, nbTypesCopy [TopAbs_SHAPE];
478 for (iType = 0; iType < TopAbs_SHAPE; ++iType)
479 nbTypesCopy[iType] = 0;
480 nbTypesCopy[aShapeCopy.ShapeType()]++;
482 TopTools_MapOfShape aMapOfShape;
483 aMapOfShape.Add(aShapeCopy);
484 TopTools_ListOfShape aListOfShape;
485 aListOfShape.Append(aShapeCopy);
487 TopTools_ListIteratorOfListOfShape itL (aListOfShape);
488 for (; itL.More(); itL.Next()) {
489 TopoDS_Iterator it (itL.Value());
490 for (; it.More(); it.Next()) {
491 TopoDS_Shape s = it.Value();
492 if (aMapOfShape.Add(s)) {
493 aListOfShape.Append(s);
494 nbTypesCopy[s.ShapeType()]++;
500 for (iType = 0; iType < TopAbs_SHAPE && isEqual; ++iType) {
501 if (nbTypes[iType] != nbTypesCopy[iType])
507 // END: Mantis issue 0021060
509 //Alternative case to check shape result Mantis 0020604: EDF 1172
510 /* TopoDS_Iterator It (aShape, Standard_True, Standard_True);
512 for (; It.More(); It.Next())
515 Standard_ConstructionError::Raise("Boolean operation aborted : result object is empty compound");*/
516 //end of 0020604: EDF 1172
517 //! the changes temporary commented because of customer needs (see the same mantis bug)
519 aFunction->SetValue(aShape);
521 log.SetTouched(Label());
527 //=======================================================================
528 //function : GEOMImpl_BooleanDriver_Type_
530 //=======================================================================
531 Standard_EXPORT Handle_Standard_Type& GEOMImpl_BooleanDriver_Type_()
533 static Handle_Standard_Type aType1 = STANDARD_TYPE(TFunction_Driver);
534 if ( aType1.IsNull()) aType1 = STANDARD_TYPE(TFunction_Driver);
535 static Handle_Standard_Type aType2 = STANDARD_TYPE(MMgt_TShared);
536 if ( aType2.IsNull()) aType2 = STANDARD_TYPE(MMgt_TShared);
537 static Handle_Standard_Type aType3 = STANDARD_TYPE(Standard_Transient);
538 if ( aType3.IsNull()) aType3 = STANDARD_TYPE(Standard_Transient);
540 static Handle_Standard_Transient _Ancestors[]= {aType1,aType2,aType3,NULL};
541 static Handle_Standard_Type _aType = new Standard_Type("GEOMImpl_BooleanDriver",
542 sizeof(GEOMImpl_BooleanDriver),
544 (Standard_Address)_Ancestors,
545 (Standard_Address)NULL);
550 //=======================================================================
551 //function : DownCast
553 //=======================================================================
554 const Handle(GEOMImpl_BooleanDriver) Handle(GEOMImpl_BooleanDriver)::DownCast(const Handle(Standard_Transient)& AnObject)
556 Handle(GEOMImpl_BooleanDriver) _anOtherObject;
558 if (!AnObject.IsNull()) {
559 if (AnObject->IsKind(STANDARD_TYPE(GEOMImpl_BooleanDriver))) {
560 _anOtherObject = Handle(GEOMImpl_BooleanDriver)((Handle(GEOMImpl_BooleanDriver)&)AnObject);
564 return _anOtherObject;