1 // Created by: Peter KURNEV
2 // Copyright (c) 2010-2014 OPEN CASCADE SAS
3 // Copyright (c) 2007-2010 CEA/DEN, EDF R&D, OPEN CASCADE
4 // Copyright (c) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, CEDRAT,
5 // EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 // This file is part of Open CASCADE Technology software library.
9 // This library is free software; you can redistribute it and/or modify it under
10 // the terms of the GNU Lesser General Public License version 2.1 as published
11 // by the Free Software Foundation, with special exception defined in the file
12 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
13 // distribution for complete text of the license and disclaimer of any warranty.
15 // Alternatively, this file may be used under the terms of Open CASCADE
16 // commercial license or contractual agreement.
19 #include <BOPAlgo_Builder.hxx>
20 #include <BOPAlgo_Alerts.hxx>
21 #include <BOPAlgo_BuilderSolid.hxx>
22 #include <BOPAlgo_PaveFiller.hxx>
23 #include <BOPAlgo_Tools.hxx>
24 #include <BOPDS_DS.hxx>
25 #include <BOPDS_ShapeInfo.hxx>
26 #include <BOPTools_AlgoTools.hxx>
27 #include <BRep_Builder.hxx>
28 #include <IntTools_Context.hxx>
29 #include <Standard_ErrorHandler.hxx>
30 #include <Standard_Failure.hxx>
32 #include <TopExp_Explorer.hxx>
34 #include <TopoDS_Compound.hxx>
35 #include <TopoDS_Solid.hxx>
36 #include <TopTools_IndexedMapOfShape.hxx>
37 #include <TopTools_MapOfOrientedShape.hxx>
39 //=======================================================================
42 //=======================================================================
43 BOPAlgo_Builder::BOPAlgo_Builder()
45 BOPAlgo_BuilderShape(),
46 myArguments(myAllocator),
47 myMapFence(100, myAllocator),
51 myImages(100, myAllocator),
52 myShapesSD(100, myAllocator),
53 myOrigins(100, myAllocator),
54 myInParts(100, myAllocator),
55 myNonDestructive(Standard_False),
56 myGlue(BOPAlgo_GlueOff),
57 myCheckInverted(Standard_True)
60 //=======================================================================
63 //=======================================================================
64 BOPAlgo_Builder::BOPAlgo_Builder
65 (const Handle(NCollection_BaseAllocator)& theAllocator)
67 BOPAlgo_BuilderShape(theAllocator),
68 myArguments(myAllocator),
69 myMapFence(100, myAllocator),
73 myImages(100, myAllocator),
74 myShapesSD(100, myAllocator),
75 myOrigins(100, myAllocator),
76 myInParts(100, myAllocator),
77 myNonDestructive(Standard_False),
78 myGlue(BOPAlgo_GlueOff),
79 myCheckInverted(Standard_True)
82 //=======================================================================
85 //=======================================================================
86 BOPAlgo_Builder::~BOPAlgo_Builder()
88 if (myEntryPoint==1) {
95 //=======================================================================
98 //=======================================================================
99 void BOPAlgo_Builder::Clear()
101 BOPAlgo_BuilderShape::Clear();
109 //=======================================================================
110 //function : AddArgument
112 //=======================================================================
113 void BOPAlgo_Builder::AddArgument(const TopoDS_Shape& theShape)
115 if (myMapFence.Add(theShape)) {
116 myArguments.Append(theShape);
119 //=======================================================================
120 //function : SetArguments
122 //=======================================================================
123 void BOPAlgo_Builder::SetArguments(const TopTools_ListOfShape& theShapes)
125 TopTools_ListIteratorOfListOfShape aIt;
129 aIt.Initialize(theShapes);
130 for (; aIt.More(); aIt.Next()) {
131 const TopoDS_Shape& aS = aIt.Value();
135 //=======================================================================
136 // function: CheckData
138 //=======================================================================
139 void BOPAlgo_Builder::CheckData()
141 Standard_Integer aNb = myArguments.Extent();
143 AddError (new BOPAlgo_AlertTooFewArguments); // too few arguments to process
149 //=======================================================================
150 // function: CheckFiller
152 //=======================================================================
153 void BOPAlgo_Builder::CheckFiller()
156 AddError (new BOPAlgo_AlertNoFiller);
159 GetReport()->Merge (myPaveFiller->GetReport());
162 //=======================================================================
165 //=======================================================================
166 void BOPAlgo_Builder::Prepare()
171 // 1. myShape is empty compound
172 aBB.MakeCompound(aC);
175 //=======================================================================
178 //=======================================================================
179 void BOPAlgo_Builder::Perform(const Message_ProgressRange& theRange)
181 GetReport()->Clear();
183 if (myEntryPoint==1) {
190 Handle(NCollection_BaseAllocator) aAllocator=
191 NCollection_BaseAllocator::CommonBaseAllocator();
193 BOPAlgo_PaveFiller* pPF=new BOPAlgo_PaveFiller(aAllocator);
195 pPF->SetArguments(myArguments);
196 pPF->SetRunParallel(myRunParallel);
197 Message_ProgressScope aPS(theRange, "Performing General Fuse operation", 10);
198 pPF->SetFuzzyValue(myFuzzyValue);
199 pPF->SetNonDestructive(myNonDestructive);
200 pPF->SetGlue(myGlue);
201 pPF->SetUseOBB(myUseOBB);
203 pPF->Perform(aPS.Next(9));
206 PerformInternal(*pPF, aPS.Next(1));
208 //=======================================================================
209 //function : PerformWithFiller
211 //=======================================================================
212 void BOPAlgo_Builder::PerformWithFiller(const BOPAlgo_PaveFiller& theFiller, const Message_ProgressRange& theRange)
214 GetReport()->Clear();
216 myNonDestructive = theFiller.NonDestructive();
217 myFuzzyValue = theFiller.FuzzyValue();
218 myGlue = theFiller.Glue();
219 myUseOBB = theFiller.UseOBB();
220 PerformInternal(theFiller, theRange);
222 //=======================================================================
223 //function : PerformInternal
225 //=======================================================================
226 void BOPAlgo_Builder::PerformInternal(const BOPAlgo_PaveFiller& theFiller, const Message_ProgressRange& theRange)
228 GetReport()->Clear();
232 PerformInternal1(theFiller, theRange);
235 catch (Standard_Failure const&) {
236 AddError (new BOPAlgo_AlertBuilderFailed);
240 //=======================================================================
241 //function : getNbShapes
243 //=======================================================================
244 BOPAlgo_Builder::NbShapes BOPAlgo_Builder::getNbShapes() const
247 aCounter.NbVertices() = myDS->ShapesSD().Size();
248 for (Standard_Integer i = 0; i < myDS->NbSourceShapes(); ++i)
250 const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(i);
251 switch (aSI.ShapeType())
255 if (myDS->HasPaveBlocks(i))
257 aCounter.NbEdges()++;
262 aCounter.NbWires()++;
266 if (myDS->HasFaceInfo(i))
268 aCounter.NbFaces()++;
273 aCounter.NbShells()++;
276 aCounter.NbSolids()++;
278 case TopAbs_COMPSOLID:
279 aCounter.NbCompsolids()++;
281 case TopAbs_COMPOUND:
282 aCounter.NbCompounds()++;
290 //=======================================================================
291 // function: fillPIConstants
293 //=======================================================================
294 void BOPAlgo_Builder::fillPIConstants (const Standard_Real theWhole,
295 BOPAlgo_PISteps& theSteps) const
297 // Fill in the constants:
300 // for FillHistroty, which takes about 5% of the whole operation
301 theSteps.SetStep(PIOperation_FillHistory, 0.05 * theWhole);
304 // and for PostTreat, which takes about 3% of the whole operation
305 theSteps.SetStep(PIOperation_PostTreat, 0.03 * theWhole);
308 //=======================================================================
309 // function: fillPISteps
311 //=======================================================================
312 void BOPAlgo_Builder::fillPISteps (BOPAlgo_PISteps& theSteps) const
314 // Compute the rest of the operations - all depend on the number of sub-shapes of certain type
315 NbShapes aNbShapes = getNbShapes();
317 theSteps.SetStep(PIOperation_TreatVertices, aNbShapes.NbVertices());
318 theSteps.SetStep(PIOperation_TreatEdges, aNbShapes.NbEdges());
319 theSteps.SetStep(PIOperation_TreatWires, aNbShapes.NbWires());
320 theSteps.SetStep(PIOperation_TreatFaces, 20 * aNbShapes.NbFaces());
321 theSteps.SetStep(PIOperation_TreatShells, aNbShapes.NbShells());
322 theSteps.SetStep(PIOperation_TreatSolids, 50 * aNbShapes.NbSolids());
323 theSteps.SetStep(PIOperation_TreatCompsolids, aNbShapes.NbCompsolids());
324 theSteps.SetStep(PIOperation_TreatCompounds, aNbShapes.NbCompounds());
327 //=======================================================================
328 //function : PerformInternal1
330 //=======================================================================
331 void BOPAlgo_Builder::PerformInternal1(const BOPAlgo_PaveFiller& theFiller, const Message_ProgressRange& theRange)
333 myPaveFiller=(BOPAlgo_PaveFiller*)&theFiller;
334 myDS=myPaveFiller->PDS();
335 myContext=myPaveFiller->Context();
336 myFuzzyValue = myPaveFiller->FuzzyValue();
337 myNonDestructive = myPaveFiller->NonDestructive();
339 Message_ProgressScope aPS(theRange, "Building the result of General Fuse operation", 100);
352 BOPAlgo_PISteps aSteps(PIOperation_Last);
353 analyzeProgress(100., aSteps);
356 FillImagesVertices(aPS.Next(aSteps.GetStep(PIOperation_TreatVertices)));
361 BuildResult(TopAbs_VERTEX);
366 FillImagesEdges(aPS.Next(aSteps.GetStep(PIOperation_TreatEdges)));
371 BuildResult(TopAbs_EDGE);
377 FillImagesContainers(TopAbs_WIRE, aPS.Next(aSteps.GetStep(PIOperation_TreatWires)));
382 BuildResult(TopAbs_WIRE);
388 FillImagesFaces(aPS.Next(aSteps.GetStep(PIOperation_TreatFaces)));
393 BuildResult(TopAbs_FACE);
398 FillImagesContainers(TopAbs_SHELL, aPS.Next(aSteps.GetStep(PIOperation_TreatShells)));
403 BuildResult(TopAbs_SHELL);
408 FillImagesSolids(aPS.Next(aSteps.GetStep(PIOperation_TreatSolids)));
413 BuildResult(TopAbs_SOLID);
418 FillImagesContainers(TopAbs_COMPSOLID, aPS.Next(aSteps.GetStep(PIOperation_TreatCompsolids)));
423 BuildResult(TopAbs_COMPSOLID);
429 FillImagesCompounds(aPS.Next(aSteps.GetStep(PIOperation_TreatCompounds)));
434 BuildResult(TopAbs_COMPOUND);
440 PrepareHistory(aPS.Next(aSteps.GetStep(PIOperation_FillHistory)));
446 PostTreat(aPS.Next(aSteps.GetStep(PIOperation_PostTreat)));
449 //=======================================================================
450 //function : PostTreat
452 //=======================================================================
453 void BOPAlgo_Builder::PostTreat(const Message_ProgressRange& theRange)
455 Standard_Integer i, aNbS;
456 TopAbs_ShapeEnum aType;
457 TopTools_IndexedMapOfShape aMA;
458 if (myPaveFiller->NonDestructive()) {
460 aNbS=myDS->NbSourceShapes();
461 for (i=0; i<aNbS; ++i) {
462 const BOPDS_ShapeInfo& aSI=myDS->ShapeInfo(i);
463 aType=aSI.ShapeType();
464 if (aType==TopAbs_VERTEX ||
466 aType==TopAbs_FACE) {
467 const TopoDS_Shape& aS=aSI.Shape();
473 Message_ProgressScope aPS(theRange, "Post treatment of result shape", 2);
474 BOPTools_AlgoTools::CorrectTolerances(myShape, aMA, 0.05, myRunParallel);
476 BOPTools_AlgoTools::CorrectShapeTolerances(myShape, aMA, myRunParallel);
479 //=======================================================================
480 //function : BuildBOP
482 //=======================================================================
483 void BOPAlgo_Builder::BuildBOP(const TopTools_ListOfShape& theObjects,
484 const TopAbs_State theObjState,
485 const TopTools_ListOfShape& theTools,
486 const TopAbs_State theToolsState,
487 const Message_ProgressRange& theRange,
488 Handle(Message_Report) theReport)
493 // Report for the method
494 Handle(Message_Report) aReport = theReport.IsNull() ? myReport : theReport;
496 if (myArguments.IsEmpty() || myShape.IsNull())
498 aReport->AddAlert(Message_Fail, new BOPAlgo_AlertBuilderFailed());
501 // Check the input data
502 if ((theObjState != TopAbs_IN && theObjState != TopAbs_OUT) ||
503 (theToolsState != TopAbs_IN && theToolsState != TopAbs_OUT))
505 aReport->AddAlert(Message_Fail, new BOPAlgo_AlertBOPNotSet());
509 // Check input shapes
510 Standard_Boolean hasObjects = !theObjects.IsEmpty();
511 Standard_Boolean hasTools = !theTools .IsEmpty();
512 if (!hasObjects && !hasTools)
514 aReport->AddAlert(Message_Fail, new BOPAlgo_AlertTooFewArguments());
518 // Check that all input solids are from the arguments
519 for (Standard_Integer i = 0; i < 2; ++i)
521 const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
522 TopTools_ListOfShape::Iterator itLS(aList);
523 for (; itLS.More(); itLS.Next())
525 const TopoDS_Shape& aS = itLS.Value();
526 // Check if the shape belongs to the arguments of operation
527 if (myDS->Index(aS) < 0)
529 aReport->AddAlert(Message_Fail, new BOPAlgo_AlertUnknownShape(aS));
533 // Check if the shape is a solid or collection of them
534 if (aS.ShapeType() != TopAbs_SOLID)
536 TopTools_ListOfShape aLS;
537 TopTools_MapOfShape aMFence;
538 BOPTools_AlgoTools::TreatCompound(aS, aLS, &aMFence);
540 TopTools_ListOfShape::Iterator it(aLS);
541 for (; it.More(); it.Next())
543 const TopoDS_Shape& aSx = it.Value();
544 if (aSx.ShapeType() != TopAbs_SOLID &&
545 aSx.ShapeType() != TopAbs_COMPSOLID)
547 aReport->AddAlert(Message_Fail, new BOPAlgo_AlertUnsupportedType(aS));
555 // Classification of the faces relatively solids has been made
556 // on the stage of Solids splitting. All results are saved into
557 // myInParts map, which connects the solids with its IN faces from
558 // other arguments. All faces not contained in the list of IN faces
559 // will be considered as OUT.
561 // Prepare the maps of splits of solids faces with orientations
562 TopTools_IndexedMapOfOrientedShape aMObjFacesOri, aMToolFacesOri;
563 // Prepare the maps of splits of solids faces
564 TopTools_IndexedMapOfShape aMObjFaces, aMToolFaces;
565 // Copy the list of IN faces of the solids into map
566 TopTools_MapOfShape anINObjects, anINTools;
568 for (Standard_Integer i = 0; i < 2; ++i)
570 const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
571 TopTools_IndexedMapOfOrientedShape& aMapOri = !i ? aMObjFacesOri : aMToolFacesOri;
572 TopTools_IndexedMapOfShape& aMap = !i ? aMObjFaces : aMToolFaces;
573 TopTools_ListOfShape::Iterator itLS(aList);
574 for (; itLS.More(); itLS.Next())
576 const TopoDS_Shape& aShape = itLS.Value();
577 TopExp_Explorer expS(aShape, TopAbs_SOLID);
578 for (; expS.More(); expS.Next())
580 const TopoDS_Shape& aS = expS.Current();
581 TopExp_Explorer expF(aS, TopAbs_FACE);
582 for (; expF.More(); expF.Next())
584 const TopoDS_Shape& aF = expF.Current();
585 if (aF.Orientation() != TopAbs_FORWARD &&
586 aF.Orientation() != TopAbs_REVERSED)
588 const TopTools_ListOfShape* pLFIm = myImages.Seek(aF);
591 TopTools_ListOfShape::Iterator itLFIm(*pLFIm);
592 for (; itLFIm.More(); itLFIm.Next())
594 TopoDS_Face aFIm = TopoDS::Face(itLFIm.Value());
595 if (BOPTools_AlgoTools::IsSplitToReverse(aFIm, aF, myContext))
608 // Copy the list of IN faces into a map
609 const TopTools_ListOfShape* pLFIN = myInParts.Seek(aS);
612 TopTools_MapOfShape& anINMap = !i ? anINObjects : anINTools;
613 TopTools_ListOfShape::Iterator itLFIn(*pLFIN);
614 for (; itLFIn.More(); itLFIn.Next())
615 anINMap.Add(itLFIn.Value());
621 // Now we need to select all faces which will participate in
622 // building of the resulting solids. The final set of faces
623 // depends on the given states for the groups.
624 Standard_Boolean isObjectsIN = (theObjState == TopAbs_IN),
625 isToolsIN = (theToolsState == TopAbs_IN);
628 Standard_Boolean bAvoidIN = (!isObjectsIN && !isToolsIN), // avoid all in faces
629 bAvoidINforBoth = (isObjectsIN != isToolsIN); // avoid faces IN for both groups
631 // Choose which SD faces are needed to be taken - equally or differently oriented faces
632 Standard_Boolean isSameOriNeeded = (theObjState == theToolsState);
634 TopTools_IndexedMapOfOrientedShape aMResFacesOri;
635 TopTools_MapOfShape aMResFacesFence;
637 TopTools_MapOfShape aMFence, aMFToAvoid;
638 // Oriented fence map
639 TopTools_MapOfOrientedShape aMFenceOri;
641 for (Standard_Integer i = 0; i < 2; ++i)
643 const TopTools_IndexedMapOfOrientedShape& aMap = !i ? aMObjFacesOri : aMToolFacesOri;
644 const TopTools_IndexedMapOfShape& anOppositeMap = !i ? aMToolFaces : aMObjFaces;
645 const TopTools_MapOfShape& anINMap = !i ? anINObjects : anINTools;
646 const TopTools_MapOfShape& anOppositeINMap = !i ? anINTools : anINObjects;
647 const Standard_Boolean bTakeIN = !i ? isObjectsIN : isToolsIN;
649 const Standard_Integer aNbF = aMap.Extent();
650 for (Standard_Integer j = 1; j <= aNbF; ++j)
652 TopoDS_Shape aFIm = aMap(j);
654 Standard_Boolean isIN = anINMap.Contains(aFIm);
655 Standard_Boolean isINOpposite = anOppositeINMap.Contains(aFIm);
657 // Filtering for FUSE - avoid any IN faces
658 if (bAvoidIN && (isIN || isINOpposite))
661 // Filtering for CUT - avoid faces IN for both groups
662 if (bAvoidINforBoth && isIN && isINOpposite)
665 // Treatment of SD faces
666 if (!aMFence.Add(aFIm))
668 if (!anOppositeMap.Contains(aFIm))
670 // The face belongs to only one group
671 if (bTakeIN != isSameOriNeeded)
672 aMFToAvoid.Add(aFIm);
676 // The face belongs to both groups.
677 // Using its orientation decide if it is needed in the result or not.
678 Standard_Boolean isSameOri = !aMFenceOri.Add(aFIm);
679 if (isSameOriNeeded == isSameOri)
681 // Take the shape without classification
682 if (aMResFacesFence.Add(aFIm))
683 aMResFacesOri.Add(aFIm);
687 aMFToAvoid.Add(aFIm);
692 if (!aMFenceOri.Add(aFIm))
695 if (bTakeIN == isINOpposite)
699 aMResFacesOri.Add(aFIm);
700 aMResFacesOri.Add(aFIm.Reversed());
702 else if (bTakeIN && !isSameOriNeeded)
703 aMResFacesOri.Add(aFIm.Reversed());
705 aMResFacesOri.Add(aFIm);
706 aMResFacesFence.Add(aFIm);
711 // Remove the faces which has to be avoided
712 TopTools_ListOfShape aResFaces;
713 const Standard_Integer aNbRF = aMResFacesOri.Extent();
714 for (Standard_Integer i = 1; i <= aNbRF; ++i)
716 const TopoDS_Shape& aRF = aMResFacesOri(i);
717 if (!aMFToAvoid.Contains(aRF))
718 aResFaces.Append(aRF);
720 Message_ProgressScope aPS(theRange, NULL, 2);
723 // Try to build closed solids from the faces
724 BOPAlgo_BuilderSolid aBS;
725 aBS.SetShapes(aResFaces);
726 aBS.SetRunParallel(myRunParallel);
727 aBS.SetContext(myContext);
728 aBS.SetFuzzyValue(myFuzzyValue);
729 aBS.Perform(aPS.Next());
732 TopTools_ListOfShape aResSolids;
735 if (!aBS.HasErrors())
737 // If any, add solids into resulting compound
738 TopTools_ListIteratorOfListOfShape itA(aBS.Areas());
739 for (; itA.More(); itA.Next())
741 const TopoDS_Shape& aSolid = itA.Value();
742 // The solid must contain at least one face
743 // from either of objects or tools
744 TopExp_Explorer expF(aSolid, TopAbs_FACE);
745 for (; expF.More(); expF.Next())
747 const TopoDS_Shape& aF = expF.Current();
748 if (aMObjFacesOri.Contains(aF) || aMToolFacesOri.Contains(aF))
753 aResSolids.Append(aSolid);
754 TopExp::MapShapes(aSolid, aMFence);
763 // Collect unused faces
764 TopoDS_Compound anUnUsedFaces;
765 aBB.MakeCompound(anUnUsedFaces);
767 TopTools_ListOfShape::Iterator itLF(aResFaces);
768 for (; itLF.More(); itLF.Next())
770 if (aMFence.Add(itLF.Value()))
771 aBB.Add(anUnUsedFaces, itLF.Value());
774 // Build blocks from the unused faces
775 TopTools_ListOfShape aLCB;
776 BOPTools_AlgoTools::MakeConnexityBlocks(anUnUsedFaces, TopAbs_EDGE, TopAbs_FACE, aLCB);
778 // Build solid from each block
779 TopTools_ListIteratorOfListOfShape itCB(aLCB);
780 for (; itCB.More(); itCB.Next())
782 const TopoDS_Shape& aCB = itCB.Value();
784 aBB.MakeShell(aShell);
785 // Add faces of the block to the shell
786 TopExp_Explorer anExpF(aCB, TopAbs_FACE);
787 for (; anExpF.More(); anExpF.Next())
788 aBB.Add(aShell, TopoDS::Face(anExpF.Current()));
790 BOPTools_AlgoTools::OrientFacesOnShell(aShell);
791 // Make solid out of the shell
793 aBB.MakeSolid(aSolid);
794 aBB.Add(aSolid, aShell);
795 // Add new solid to result
796 aResSolids.Append(aSolid);
801 // Fill solids with internal parts coming with the solids
802 TopTools_ListOfShape anInParts;
803 for (Standard_Integer i = 0; i < 2; ++i)
805 const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
806 TopTools_ListOfShape::Iterator itLS(aList);
807 for (; itLS.More(); itLS.Next())
809 TopExp_Explorer expS(itLS.Value(), TopAbs_SOLID);
810 for (; expS.More(); expS.Next())
812 const TopoDS_Shape& aS = expS.Current(); // Solid
813 for (TopoDS_Iterator it(aS); it.More(); it.Next())
815 const TopoDS_Shape& aSInt = it.Value();
816 if (aSInt.Orientation() == TopAbs_INTERNAL)
817 anInParts.Append(aSInt); // vertex or edge
821 TopoDS_Iterator itInt(aSInt);
822 if (itInt.More() && itInt.Value().Orientation() == TopAbs_INTERNAL)
823 anInParts.Append(aSInt);
830 BOPAlgo_Tools::FillInternals(aResSolids, anInParts, myImages, myContext);
833 // Combine solids into compound
834 TopoDS_Shape aResult;
835 aBB.MakeCompound(TopoDS::Compound(aResult));
837 TopTools_ListOfShape::Iterator itLS(aResSolids);
838 for (; itLS.More(); itLS.Next())
839 aBB.Add(aResult, itLS.Value());
842 PrepareHistory(aPS.Next());