1 // Copyright (C) 2007-2012 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
22 #include <GEOMImpl_BooleanDriver.hxx>
23 #include <GEOMImpl_IBoolean.hxx>
24 #include <GEOMImpl_Types.hxx>
25 #include <GEOMImpl_GlueDriver.hxx>
26 #include <GEOM_Function.hxx>
27 #include <GEOMUtils.hxx>
29 #include <TNaming_CopyShape.hxx>
31 #include <ShapeFix_ShapeTolerance.hxx>
32 #include <ShapeFix_Shape.hxx>
34 #include <BRep_Builder.hxx>
35 #include <BRepAlgo.hxx>
36 #include <BRepAlgoAPI_Common.hxx>
37 #include <BRepAlgoAPI_Cut.hxx>
38 #include <BRepAlgoAPI_Fuse.hxx>
39 #include <BRepAlgoAPI_Section.hxx>
40 #include <BRepCheck_Analyzer.hxx>
42 #include <TopExp_Explorer.hxx>
43 #include <TopoDS_Shape.hxx>
44 #include <TopoDS_Compound.hxx>
45 #include <TopoDS_Iterator.hxx>
46 #include <TopTools_MapOfShape.hxx>
47 #include <TopTools_ListOfShape.hxx>
48 #include <TopTools_ListIteratorOfListOfShape.hxx>
50 #include <TColStd_IndexedDataMapOfTransientTransient.hxx>
52 #include <Precision.hxx>
54 #include <Standard_ConstructionError.hxx>
55 #include <StdFail_NotDone.hxx>
57 //=======================================================================
60 //=======================================================================
61 const Standard_GUID& GEOMImpl_BooleanDriver::GetID()
63 static Standard_GUID aBooleanDriver("FF1BBB21-5D14-4df2-980B-3A668264EA16");
64 return aBooleanDriver;
67 //=======================================================================
68 //function : GEOMImpl_BooleanDriver
70 //=======================================================================
71 GEOMImpl_BooleanDriver::GEOMImpl_BooleanDriver()
75 //=======================================================================
78 //=======================================================================
79 Standard_Integer GEOMImpl_BooleanDriver::Execute (TFunction_Logbook& log) const
81 if (Label().IsNull()) return 0;
82 Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
84 GEOMImpl_IBoolean aCI (aFunction);
85 Standard_Integer aType = aFunction->GetType();
89 Handle(GEOM_Function) aRefShape1 = aCI.GetShape1();
90 Handle(GEOM_Function) aRefShape2 = aCI.GetShape2();
91 TopoDS_Shape aShape1 = aRefShape1->GetValue();
92 TopoDS_Shape aShape2 = aRefShape2->GetValue();
94 if (!aShape1.IsNull() && !aShape2.IsNull()) {
95 // check arguments for Mantis issue 0021019
96 BRepCheck_Analyzer ana (aShape1, Standard_True);
98 StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
101 StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
103 // perform COMMON operation
104 if (aType == BOOLEAN_COMMON) {
109 TopTools_ListOfShape listShape1, listShape2;
110 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
111 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
113 Standard_Boolean isCompound =
114 (listShape1.Extent() > 1 || listShape2.Extent() > 1);
116 TopTools_ListIteratorOfListOfShape itSub1 (listShape1);
117 for (; itSub1.More(); itSub1.Next()) {
118 TopoDS_Shape aValue1 = itSub1.Value();
119 TopTools_ListIteratorOfListOfShape itSub2 (listShape2);
120 for (; itSub2.More(); itSub2.Next()) {
121 TopoDS_Shape aValue2 = itSub2.Value();
122 BRepAlgoAPI_Common BO (aValue1, aValue2);
124 StdFail_NotDone::Raise("Common operation can not be performed on the given shapes");
127 TopoDS_Shape aStepResult = BO.Shape();
129 // check result of this step: if it is a compound (boolean operations
130 // allways return a compound), we add all sub-shapes of it.
131 // This allows to avoid adding empty compounds,
132 // resulting from COMMON on two non-intersecting shapes.
133 if (aStepResult.ShapeType() == TopAbs_COMPOUND) {
134 TopoDS_Iterator aCompIter (aStepResult);
135 for (; aCompIter.More(); aCompIter.Next()) {
136 // add shape in a result
137 B.Add(C, aCompIter.Value());
141 // add shape in a result
142 B.Add(C, aStepResult);
152 TopTools_ListOfShape listShapeC;
153 GEOMUtils::AddSimpleShapes(C, listShapeC);
154 TopTools_ListIteratorOfListOfShape itSubC (listShapeC);
155 bool isOnlySolids = true;
156 for (; itSubC.More(); itSubC.Next()) {
157 TopoDS_Shape aValueC = itSubC.Value();
158 if (aValueC.ShapeType() != TopAbs_SOLID) isOnlySolids = false;
161 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
166 // As GlueFaces has been improved to keep all kind of shapes
167 TopExp_Explorer anExp (C, TopAbs_VERTEX);
169 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
175 // perform CUT operation
176 else if (aType == BOOLEAN_CUT) {
181 TopTools_ListOfShape listShapes, listTools;
182 GEOMUtils::AddSimpleShapes(aShape1, listShapes);
183 GEOMUtils::AddSimpleShapes(aShape2, listTools);
185 Standard_Boolean isCompound = (listShapes.Extent() > 1);
187 TopTools_ListIteratorOfListOfShape itSub1 (listShapes);
188 for (; itSub1.More(); itSub1.Next()) {
189 TopoDS_Shape aCut = itSub1.Value();
191 TopTools_ListIteratorOfListOfShape itSub2 (listTools);
192 for (; itSub2.More(); itSub2.Next()) {
193 TopoDS_Shape aTool = itSub2.Value();
194 BRepAlgoAPI_Cut BO (aCut, aTool);
196 StdFail_NotDone::Raise("Cut operation can not be performed on the given shapes");
201 // check result of this step: if it is a compound (boolean operations
202 // allways return a compound), we add all sub-shapes of it.
203 // This allows to avoid adding empty compounds,
204 // resulting from CUT of parts
205 if (aCut.ShapeType() == TopAbs_COMPOUND) {
206 TopoDS_Iterator aCompIter (aCut);
207 for (; aCompIter.More(); aCompIter.Next()) {
208 // add shape in a result
209 B.Add(C, aCompIter.Value());
213 // add shape in a result
223 TopTools_ListOfShape listShapeC;
224 GEOMUtils::AddSimpleShapes(C, listShapeC);
225 TopTools_ListIteratorOfListOfShape itSubC (listShapeC);
226 bool isOnlySolids = true;
227 for (; itSubC.More(); itSubC.Next()) {
228 TopoDS_Shape aValueC = itSubC.Value();
229 if (aValueC.ShapeType() != TopAbs_SOLID) isOnlySolids = false;
232 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
237 // As GlueFaces has been improved to keep all kind of shapes
238 TopExp_Explorer anExp (C, TopAbs_VERTEX);
240 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
246 // perform FUSE operation
247 else if (aType == BOOLEAN_FUSE) {
248 /* Fix for NPAL15379: refused
250 TopTools_ListOfShape listShape1, listShape2;
251 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
252 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
254 Standard_Boolean isIntersect = Standard_False;
256 if (listShape1.Extent() > 1 && !isIntersect) {
257 // check intersections inside the first compound
258 TopTools_ListIteratorOfListOfShape it1 (listShape1);
259 for (; it1.More() && !isIntersect; it1.Next()) {
260 TopoDS_Shape aValue1 = it1.Value();
261 TopTools_ListIteratorOfListOfShape it2 (listShape1);
262 for (; it2.More() && !isIntersect; it2.Next()) {
263 TopoDS_Shape aValue2 = it2.Value();
264 if (aValue2 != aValue1) {
265 BRepAlgoAPI_Section BO (aValue1, aValue2);
267 TopoDS_Shape aSect = BO.Shape();
268 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
270 isIntersect = Standard_True;
278 if (listShape2.Extent() > 1 && !isIntersect) {
279 // check intersections inside the second compound
280 TopTools_ListIteratorOfListOfShape it1 (listShape2);
281 for (; it1.More() && !isIntersect; it1.Next()) {
282 TopoDS_Shape aValue1 = it1.Value();
283 TopTools_ListIteratorOfListOfShape it2 (listShape2);
284 for (; it2.More() && !isIntersect; it2.Next()) {
285 TopoDS_Shape aValue2 = it2.Value();
286 if (aValue2 != aValue1) {
287 BRepAlgoAPI_Section BO (aValue1, aValue2);
289 TopoDS_Shape aSect = BO.Shape();
290 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
292 isIntersect = Standard_True;
301 // have intersections inside compounds
302 // check intersections between compounds
303 TopTools_ListIteratorOfListOfShape it1 (listShape1);
304 for (; it1.More(); it1.Next()) {
305 TopoDS_Shape aValue1 = it1.Value();
306 TopTools_ListIteratorOfListOfShape it2 (listShape2);
307 for (; it2.More(); it2.Next()) {
308 TopoDS_Shape aValue2 = it2.Value();
309 if (aValue2 != aValue1) {
310 BRepAlgoAPI_Section BO (aValue1, aValue2);
312 TopoDS_Shape aSect = BO.Shape();
313 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
315 StdFail_NotDone::Raise("Bad argument for Fuse: compound with intersecting sub-shapes");
325 BRepAlgoAPI_Fuse BO (aShape1, aShape2);
327 StdFail_NotDone::Raise("Fuse operation can not be performed on the given shapes");
332 // perform SECTION operation
333 else if (aType == BOOLEAN_SECTION) {
338 TopTools_ListOfShape listShape1, listShape2;
339 GEOMUtils::AddSimpleShapes(aShape1, listShape1);
340 GEOMUtils::AddSimpleShapes(aShape2, listShape2);
342 Standard_Boolean isCompound =
343 (listShape1.Extent() > 1 || listShape2.Extent() > 1);
345 TopTools_ListIteratorOfListOfShape itSub1 (listShape1);
346 for (; itSub1.More(); itSub1.Next()) {
347 TopoDS_Shape aValue1 = itSub1.Value();
348 TopTools_ListIteratorOfListOfShape itSub2 (listShape2);
349 for (; itSub2.More(); itSub2.Next()) {
350 TopoDS_Shape aValue2 = itSub2.Value();
351 BRepAlgoAPI_Section BO (aValue1, aValue2, Standard_False);
352 // Set approximation to have an attached 3D BSpline geometry to each edge,
353 // where analytic curve is not possible. Without this flag in some cases
354 // we obtain BSpline curve of degree 1 (C0), which is slowly
355 // processed by some algorithms (Partition for example).
356 BO.Approximation(Standard_True);
357 //modified by NIZNHY-PKV Tue Oct 18 14:34:16 2011f
358 BO.ComputePCurveOn1(Standard_True);
359 BO.ComputePCurveOn2(Standard_True);
360 //modified by NIZNHY-PKV Tue Oct 18 14:34:18 2011t
364 StdFail_NotDone::Raise("Section operation can not be performed on the given shapes");
367 TopoDS_Shape aStepResult = BO.Shape();
369 // check result of this step: if it is a compound (boolean operations
370 // allways return a compound), we add all sub-shapes of it.
371 // This allows to avoid adding empty compounds,
372 // resulting from SECTION on two non-intersecting shapes.
373 if (aStepResult.ShapeType() == TopAbs_COMPOUND) {
374 TopoDS_Iterator aCompIter (aStepResult);
375 for (; aCompIter.More(); aCompIter.Next()) {
376 // add shape in a result
377 B.Add(C, aCompIter.Value());
381 // add shape in a result
382 B.Add(C, aStepResult);
393 // As GlueFaces has been improved to keep all kind of shapes
394 TopExp_Explorer anExp (C, TopAbs_VERTEX);
396 aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
407 if (aShape.IsNull()) return 0;
409 // as boolean operations always produce compound, lets simplify it
410 // for the case, if it contains only one sub-shape
411 TopTools_ListOfShape listShapeRes;
412 GEOMUtils::AddSimpleShapes(aShape, listShapeRes);
413 if (listShapeRes.Extent() == 1) {
414 aShape = listShapeRes.First();
415 if (aShape.IsNull()) return 0;
418 // 08.07.2008 skl for bug 19761 from Mantis
419 BRepCheck_Analyzer ana (aShape, Standard_True);
421 if (!ana.IsValid()) {
422 ShapeFix_ShapeTolerance aSFT;
423 aSFT.LimitTolerance(aShape, Precision::Confusion(),
424 Precision::Confusion(), TopAbs_SHAPE);
425 Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape(aShape);
427 aShape = aSfs->Shape();
430 Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
432 //if (!BRepAlgo::IsValid(aShape)) {
433 // Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
436 // BEGIN: Mantis issue 0021060: always limit tolerance of BOP result
437 // 1. Get shape parameters for comparison
438 int nbTypes [TopAbs_SHAPE];
440 for (int iType = 0; iType < TopAbs_SHAPE; ++iType)
442 nbTypes[aShape.ShapeType()]++;
444 TopTools_MapOfShape aMapOfShape;
445 aMapOfShape.Add(aShape);
446 TopTools_ListOfShape aListOfShape;
447 aListOfShape.Append(aShape);
449 TopTools_ListIteratorOfListOfShape itL (aListOfShape);
450 for (; itL.More(); itL.Next()) {
451 TopoDS_Iterator it (itL.Value());
452 for (; it.More(); it.Next()) {
453 TopoDS_Shape s = it.Value();
454 if (aMapOfShape.Add(s)) {
455 aListOfShape.Append(s);
456 nbTypes[s.ShapeType()]++;
462 // 2. Limit tolerance
463 TopoDS_Shape aShapeCopy;
464 TColStd_IndexedDataMapOfTransientTransient aMapTShapes;
465 TNaming_CopyShape::CopyTool(aShape, aMapTShapes, aShapeCopy);
466 ShapeFix_ShapeTolerance aSFT;
467 aSFT.LimitTolerance(aShapeCopy, Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE);
468 Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape (aShapeCopy);
470 aShapeCopy = aSfs->Shape();
472 // 3. Check parameters
473 ana.Init(aShapeCopy);
475 int iType, nbTypesCopy [TopAbs_SHAPE];
477 for (iType = 0; iType < TopAbs_SHAPE; ++iType)
478 nbTypesCopy[iType] = 0;
479 nbTypesCopy[aShapeCopy.ShapeType()]++;
481 TopTools_MapOfShape aMapOfShape;
482 aMapOfShape.Add(aShapeCopy);
483 TopTools_ListOfShape aListOfShape;
484 aListOfShape.Append(aShapeCopy);
486 TopTools_ListIteratorOfListOfShape itL (aListOfShape);
487 for (; itL.More(); itL.Next()) {
488 TopoDS_Iterator it (itL.Value());
489 for (; it.More(); it.Next()) {
490 TopoDS_Shape s = it.Value();
491 if (aMapOfShape.Add(s)) {
492 aListOfShape.Append(s);
493 nbTypesCopy[s.ShapeType()]++;
499 for (iType = 0; iType < TopAbs_SHAPE && isEqual; ++iType) {
500 if (nbTypes[iType] != nbTypesCopy[iType])
506 // END: Mantis issue 0021060
508 //Alternative case to check shape result Mantis 0020604: EDF 1172
509 /* TopoDS_Iterator It (aShape, Standard_True, Standard_True);
511 for (; It.More(); It.Next())
514 Standard_ConstructionError::Raise("Boolean operation aborted : result object is empty compound");*/
515 //end of 0020604: EDF 1172
516 //! the changes temporary commented because of customer needs (see the same mantis bug)
518 aFunction->SetValue(aShape);
520 log.SetTouched(Label());
526 //=======================================================================
527 //function : GEOMImpl_BooleanDriver_Type_
529 //=======================================================================
530 Standard_EXPORT Handle_Standard_Type& GEOMImpl_BooleanDriver_Type_()
532 static Handle_Standard_Type aType1 = STANDARD_TYPE(TFunction_Driver);
533 if ( aType1.IsNull()) aType1 = STANDARD_TYPE(TFunction_Driver);
534 static Handle_Standard_Type aType2 = STANDARD_TYPE(MMgt_TShared);
535 if ( aType2.IsNull()) aType2 = STANDARD_TYPE(MMgt_TShared);
536 static Handle_Standard_Type aType3 = STANDARD_TYPE(Standard_Transient);
537 if ( aType3.IsNull()) aType3 = STANDARD_TYPE(Standard_Transient);
539 static Handle_Standard_Transient _Ancestors[]= {aType1,aType2,aType3,NULL};
540 static Handle_Standard_Type _aType = new Standard_Type("GEOMImpl_BooleanDriver",
541 sizeof(GEOMImpl_BooleanDriver),
543 (Standard_Address)_Ancestors,
544 (Standard_Address)NULL);
549 //=======================================================================
550 //function : DownCast
552 //=======================================================================
553 const Handle(GEOMImpl_BooleanDriver) Handle(GEOMImpl_BooleanDriver)::DownCast(const Handle(Standard_Transient)& AnObject)
555 Handle(GEOMImpl_BooleanDriver) _anOtherObject;
557 if (!AnObject.IsNull()) {
558 if (AnObject->IsKind(STANDARD_TYPE(GEOMImpl_BooleanDriver))) {
559 _anOtherObject = Handle(GEOMImpl_BooleanDriver)((Handle(GEOMImpl_BooleanDriver)&)AnObject);
563 return _anOtherObject;