Salome HOME
Merge from V6_main 15/03/2013
[modules/geom.git] / src / GEOMImpl / GEOMImpl_BooleanDriver.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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.
10 //
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.
15 //
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
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21
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>
28
29 #include <TNaming_CopyShape.hxx>
30
31 #include <ShapeFix_ShapeTolerance.hxx>
32 #include <ShapeFix_Shape.hxx>
33
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>
41
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>
49
50 #include <TColStd_IndexedDataMapOfTransientTransient.hxx>
51
52 #include <Precision.hxx>
53
54 #include <Standard_ConstructionError.hxx>
55 #include <StdFail_NotDone.hxx>
56
57 //=======================================================================
58 //function : GetID
59 //purpose  :
60 //=======================================================================
61 const Standard_GUID& GEOMImpl_BooleanDriver::GetID()
62 {
63   static Standard_GUID aBooleanDriver("FF1BBB21-5D14-4df2-980B-3A668264EA16");
64   return aBooleanDriver;
65 }
66
67 //=======================================================================
68 //function : GEOMImpl_BooleanDriver
69 //purpose  :
70 //=======================================================================
71 GEOMImpl_BooleanDriver::GEOMImpl_BooleanDriver()
72 {
73 }
74
75 //=======================================================================
76 //function : Execute
77 //purpose  :
78 //=======================================================================
79 Standard_Integer GEOMImpl_BooleanDriver::Execute (TFunction_Logbook& log) const
80 {
81   if (Label().IsNull()) return 0;
82   Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
83
84   GEOMImpl_IBoolean aCI (aFunction);
85   Standard_Integer aType = aFunction->GetType();
86
87   TopoDS_Shape aShape;
88
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();
93
94   if (!aShape1.IsNull() && !aShape2.IsNull()) {
95     // check arguments for Mantis issue 0021019
96     BRepCheck_Analyzer ana (aShape1, Standard_True);
97     if (!ana.IsValid())
98       StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
99     ana.Init(aShape2);
100     if (!ana.IsValid())
101       StdFail_NotDone::Raise("Boolean operation will not be performed, because argument shape is not valid");
102
103     // perform COMMON operation
104     if (aType == BOOLEAN_COMMON) {
105       BRep_Builder B;
106       TopoDS_Compound C;
107       B.MakeCompound(C);
108
109       TopTools_ListOfShape listShape1, listShape2;
110       GEOMUtils::AddSimpleShapes(aShape1, listShape1);
111       GEOMUtils::AddSimpleShapes(aShape2, listShape2);
112
113       Standard_Boolean isCompound =
114         (listShape1.Extent() > 1 || listShape2.Extent() > 1);
115
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);
123           if (!BO.IsDone()) {
124             StdFail_NotDone::Raise("Common operation can not be performed on the given shapes");
125           }
126           if (isCompound) {
127             TopoDS_Shape aStepResult = BO.Shape();
128
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());
138               }
139             }
140             else {
141               // add shape in a result
142               B.Add(C, aStepResult);
143             }
144           }
145           else
146             aShape = BO.Shape();
147         }
148       }
149
150       if (isCompound) {
151         /*
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;
159         }
160         if (isOnlySolids)
161           aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
162         else
163           aShape = C;
164         */
165
166         // As GlueFaces has been improved to keep all kind of shapes
167         TopExp_Explorer anExp (C, TopAbs_VERTEX);
168         if (anExp.More())
169           aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
170         else
171           aShape = C;
172       }
173     }
174
175     // perform CUT operation
176     else if (aType == BOOLEAN_CUT) {
177       BRep_Builder B;
178       TopoDS_Compound C;
179       B.MakeCompound(C);
180
181       TopTools_ListOfShape listShapes, listTools;
182       GEOMUtils::AddSimpleShapes(aShape1, listShapes);
183       GEOMUtils::AddSimpleShapes(aShape2, listTools);
184
185       Standard_Boolean isCompound = (listShapes.Extent() > 1);
186
187       TopTools_ListIteratorOfListOfShape itSub1 (listShapes);
188       for (; itSub1.More(); itSub1.Next()) {
189         TopoDS_Shape aCut = itSub1.Value();
190         // tools
191         TopTools_ListIteratorOfListOfShape itSub2 (listTools);
192         for (; itSub2.More(); itSub2.Next()) {
193           TopoDS_Shape aTool = itSub2.Value();
194           BRepAlgoAPI_Cut BO (aCut, aTool);
195           if (!BO.IsDone()) {
196             StdFail_NotDone::Raise("Cut operation can not be performed on the given shapes");
197           }
198           aCut = BO.Shape();
199         }
200         if (isCompound) {
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());
210             }
211           }
212           else {
213             // add shape in a result
214             B.Add(C, aCut);
215           }
216         }
217         else
218           aShape = aCut;
219       }
220
221       if (isCompound) {
222         /*
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;
230         }
231         if (isOnlySolids)
232           aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion());
233         else
234           aShape = C;
235         */
236
237         // As GlueFaces has been improved to keep all kind of shapes
238         TopExp_Explorer anExp (C, TopAbs_VERTEX);
239         if (anExp.More())
240           aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
241         else
242           aShape = C;
243       }
244     }
245
246     // perform FUSE operation
247     else if (aType == BOOLEAN_FUSE) {
248       /* Fix for NPAL15379: refused
249       // Check arguments
250       TopTools_ListOfShape listShape1, listShape2;
251       GEOMUtils::AddSimpleShapes(aShape1, listShape1);
252       GEOMUtils::AddSimpleShapes(aShape2, listShape2);
253
254       Standard_Boolean isIntersect = Standard_False;
255
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);
266               if (BO.IsDone()) {
267                 TopoDS_Shape aSect = BO.Shape();
268                 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
269                 if (anExp.More()) {
270                   isIntersect = Standard_True;
271                 }
272               }
273             }
274           }
275         }
276       }
277
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);
288               if (BO.IsDone()) {
289                 TopoDS_Shape aSect = BO.Shape();
290                 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
291                 if (anExp.More()) {
292                   isIntersect = Standard_True;
293                 }
294               }
295             }
296           }
297         }
298       }
299
300       if (isIntersect) {
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);
311               if (BO.IsDone()) {
312                 TopoDS_Shape aSect = BO.Shape();
313                 TopExp_Explorer anExp (aSect, TopAbs_EDGE);
314                 if (anExp.More()) {
315                   StdFail_NotDone::Raise("Bad argument for Fuse: compound with intersecting sub-shapes");
316                 }
317               }
318             }
319           }
320         }
321       }
322       */
323
324       // Perform
325       BRepAlgoAPI_Fuse BO (aShape1, aShape2);
326       if (!BO.IsDone()) {
327         StdFail_NotDone::Raise("Fuse operation can not be performed on the given shapes");
328       }
329       aShape = BO.Shape();
330     }
331
332     // perform SECTION operation
333     else if (aType == BOOLEAN_SECTION) {
334       BRep_Builder B;
335       TopoDS_Compound C;
336       B.MakeCompound(C);
337
338       TopTools_ListOfShape listShape1, listShape2;
339       GEOMUtils::AddSimpleShapes(aShape1, listShape1);
340       GEOMUtils::AddSimpleShapes(aShape2, listShape2);
341
342       Standard_Boolean isCompound =
343         (listShape1.Extent() > 1 || listShape2.Extent() > 1);
344
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
361           
362           BO.Build();
363           if (!BO.IsDone()) {
364             StdFail_NotDone::Raise("Section operation can not be performed on the given shapes");
365           }
366           if (isCompound) {
367             TopoDS_Shape aStepResult = BO.Shape();
368
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());
378               }
379             }
380             else {
381               // add shape in a result
382               B.Add(C, aStepResult);
383             }
384           }
385           else
386             aShape = BO.Shape();
387         }
388       }
389
390       if (isCompound) {
391         //aShape = C;
392
393         // As GlueFaces has been improved to keep all kind of shapes
394         TopExp_Explorer anExp (C, TopAbs_VERTEX);
395         if (anExp.More())
396           aShape = GEOMImpl_GlueDriver::GlueFaces(C, Precision::Confusion(), Standard_True);
397         else
398           aShape = C;
399       }
400     }
401
402     // UNKNOWN operation
403     else {
404     }
405   }
406
407   if (aShape.IsNull()) return 0;
408
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;
416   }
417
418   // 08.07.2008 skl for bug 19761 from Mantis
419   BRepCheck_Analyzer ana (aShape, Standard_True);
420   ana.Init(aShape);
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);
426     aSfs->Perform();
427     aShape = aSfs->Shape();
428     ana.Init(aShape);
429     if (!ana.IsValid())
430       Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
431   }
432   //if (!BRepAlgo::IsValid(aShape)) {
433   //  Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result");
434   //}
435
436   // BEGIN: Mantis issue 0021060: always limit tolerance of BOP result
437   // 1. Get shape parameters for comparison
438   int nbTypes [TopAbs_SHAPE];
439   {
440     for (int iType = 0; iType < TopAbs_SHAPE; ++iType)
441       nbTypes[iType] = 0;
442     nbTypes[aShape.ShapeType()]++;
443
444     TopTools_MapOfShape aMapOfShape;
445     aMapOfShape.Add(aShape);
446     TopTools_ListOfShape aListOfShape;
447     aListOfShape.Append(aShape);
448
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()]++;
457         }
458       }
459     }
460   }
461
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);
469   aSfs->Perform();
470   aShapeCopy = aSfs->Shape();
471
472   // 3. Check parameters
473   ana.Init(aShapeCopy);
474   if (ana.IsValid()) {
475     int iType, nbTypesCopy [TopAbs_SHAPE];
476
477     for (iType = 0; iType < TopAbs_SHAPE; ++iType)
478       nbTypesCopy[iType] = 0;
479     nbTypesCopy[aShapeCopy.ShapeType()]++;
480
481     TopTools_MapOfShape aMapOfShape;
482     aMapOfShape.Add(aShapeCopy);
483     TopTools_ListOfShape aListOfShape;
484     aListOfShape.Append(aShapeCopy);
485
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()]++;
494         }
495       }
496     }
497
498     bool isEqual = true;
499     for (iType = 0; iType < TopAbs_SHAPE && isEqual; ++iType) {
500       if (nbTypes[iType] != nbTypesCopy[iType])
501         isEqual = false;
502     }
503     if (isEqual)
504       aShape = aShapeCopy;
505   }
506   // END: Mantis issue 0021060
507
508   //Alternative case to check shape result Mantis 0020604: EDF 1172
509 /*  TopoDS_Iterator It (aShape, Standard_True, Standard_True);
510   int nbSubshapes=0;
511   for (; It.More(); It.Next())
512     nbSubshapes++;
513   if (!nbSubshapes)
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)
517
518   aFunction->SetValue(aShape);
519
520   log.SetTouched(Label());
521
522   return 1;
523 }
524
525
526 //=======================================================================
527 //function :  GEOMImpl_BooleanDriver_Type_
528 //purpose  :
529 //=======================================================================
530 Standard_EXPORT Handle_Standard_Type& GEOMImpl_BooleanDriver_Type_()
531 {
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);
538
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),
542                                                          1,
543                                                          (Standard_Address)_Ancestors,
544                                                          (Standard_Address)NULL);
545
546   return _aType;
547 }
548
549 //=======================================================================
550 //function : DownCast
551 //purpose  :
552 //=======================================================================
553 const Handle(GEOMImpl_BooleanDriver) Handle(GEOMImpl_BooleanDriver)::DownCast(const Handle(Standard_Transient)& AnObject)
554 {
555   Handle(GEOMImpl_BooleanDriver) _anOtherObject;
556
557   if (!AnObject.IsNull()) {
558     if (AnObject->IsKind(STANDARD_TYPE(GEOMImpl_BooleanDriver))) {
559       _anOtherObject = Handle(GEOMImpl_BooleanDriver)((Handle(GEOMImpl_BooleanDriver)&)AnObject);
560     }
561   }
562
563   return _anOtherObject;
564 }