0030145: Modeling Algorithms - Boolean Operations on open solids
authoremv <emv@opencascade.com>
Fri, 7 Sep 2018 12:24:49 +0000 (15:24 +0300)
committerbugmaster <bugmaster@opencascade.com>
Wed, 3 Oct 2018 16:21:14 +0000 (19:21 +0300)
Provide possibility to perform Boolean operations on open solids.

Implementation of the new method *BOPAlgo_Builder::BuildBOP* performing the construction of the result shape for the given type of Boolean operation.
This approach does not rely on the splits of solid to be correct and looks for the faces with necessary state relatively opposite solids to build the result solid.
The call to this method is performed from BOP algorithm in case there were open solids in the arguments.

Implementation of the draw command *buildbop* performing a call to the method above.

35 files changed:
dox/user_guides/boolean_operations/boolean_operations.md
dox/user_guides/draw_test_harness/draw_test_harness.md
src/BOPAlgo/BOPAlgo.msg
src/BOPAlgo/BOPAlgo_Alerts.hxx
src/BOPAlgo/BOPAlgo_BOP.cxx
src/BOPAlgo/BOPAlgo_BOP.hxx
src/BOPAlgo/BOPAlgo_BOPAlgo_msg.pxx
src/BOPAlgo/BOPAlgo_Builder.cxx
src/BOPAlgo/BOPAlgo_Builder.hxx
src/BOPAlgo/BOPAlgo_Builder_3.cxx
src/BOPAlgo/BOPAlgo_MakerVolume.cxx
src/BOPAlgo/BOPAlgo_Tools.cxx
src/BOPAlgo/BOPAlgo_Tools.hxx
src/BOPTest/BOPTest.cxx
src/BOPTest/BOPTest_PartitionCommands.cxx
src/BOPTools/BOPTools_AlgoTools.cxx
src/BRepFeat/BRepFeat_Builder.cxx
src/BRepFeat/BRepFeat_Builder.hxx
src/TopoDS/TopoDS_AlertWithShape.hxx
tests/boolean/bcut_complex/O3
tests/boolean/bcut_complex/O4
tests/boolean/bcut_complex/O5
tests/boolean/bcut_complex/O6
tests/boolean/bfuse_complex/O5
tests/boolean/grids.list
tests/boolean/opensolid/A1 [new file with mode: 0644]
tests/boolean/opensolid/A2 [new file with mode: 0644]
tests/boolean/opensolid/A3 [new file with mode: 0644]
tests/boolean/opensolid/A4 [new file with mode: 0644]
tests/boolean/opensolid/A5 [new file with mode: 0644]
tests/boolean/opensolid/A6 [new file with mode: 0644]
tests/boolean/opensolid/A7 [new file with mode: 0644]
tests/boolean/opensolid/A8 [new file with mode: 0644]
tests/boolean/opensolid/A9 [new file with mode: 0644]
tests/bugs/modalg_2/bug420

index eef0ec6..6253d38 100644 (file)
@@ -1891,6 +1891,21 @@ The input data for this step is as follows:
 | 2.3 |        Build solids <i>(SDi)</i> from *SFS*. | *BOPAlgo_BuilderSolid* |
 | 2.4 |        Add the solids <i>(SDi)</i> to the result       | |
 
+@subsection occt_algorithms_bop_on_opensolids Boolean operations on open solids
+
+The Boolean operations on open solids are tricky enough that the standard approach of Boolean operations for building the result, based on the splits of solids does not work.
+It happens because the algorithm for splitting solids (*BOPAlgo_BuilderSolid*) always tries to create the closed loops (shells) and make solids from them. But if the input solid is not closed, what can be expected from its splits?
+For performing Boolean Operations on open solids another approach is used, which does not rely on the splits of the solids to be correct, but tries to select the splits of faces, which are necessary for the given type of operation.
+The point here is that the type of Boolean operation clearly defines the states for the faces to be taken into result:
+- For **COMMON** operation all the faces from the arguments located inside any solid of the opposite group must be taken;
+- For **FUSE** operation all the faces from the arguments located outside of all solids of the opposite group must be taken;
+- For **CUT** operation all the faces from the Objects located outside of all solids of the Tools and all faces from the Tools located inside any solid of the Objects must be taken;
+- For **CUT21** operation all the faces from the Objects located inside any solid of the Tools and all faces from the Tools located outside of all solids of the Objects must be taken.
+From the selected faces the result solids are built. Please note, that the result may contain as normal (closed) solids as the open ones.
+
+Even with this approach, the correct result of Boolean operation on open solids cannot be always guaranteed.
+This is explained by non-manifold nature of open solids: in some cases classification of a face depends on the point of the face chosen for classification.
+
 @section occt_algorithms_10a Section Algorithm 
 
 @subsection occt_algorithms_10a_1 Arguments
index 32176df..241da61 100644 (file)
@@ -8370,6 +8370,62 @@ bfillds
 bsplit result
 ~~~~
 
+@subsubsection occt_draw_bop_build_BOP_opensolids Alternative command for BOP
+
+There is an alternative way to build the result of Boolean operation using the **buildbop** command, which should be run after any other building command, such as **bbuild** or **bbop** or **bsplit**.
+The command has the following features:
+* It is designed to work on open solids and thus uses the alternative approach for building the results (see @ref occt_algorithms_bop_on_opensolids "BOP on open solids" chapter of Boolean operations user guide).
+* It allows changing the groups of Objects and Tools of the operation (even excluding some of the arguments is possible).
+* History information for solids will be lost.
+
+Syntax:
+~~~~
+buildbop result -o s1 [s2 ...] -t s3 [s4 ...] -op operation (common/fuse/cut/tuc)
+Where:
+result      - result shape of the operation
+s1 s2 s3 s4 - arguments (solids) of the GF operation
+operation   - type of boolean operation
+~~~~
+
+**Example**
+~~~~
+box b1 10 10 10
+box b2 5 5 5 10 10 10
+box b3 -5 -5 -5 10 10 10
+
+bclearobjects
+bcleartools
+baddobjects b1 b2 b3
+bfillds
+bbuild r
+
+# bbop command will not be available as the tools are not set
+# but buildbop is available
+
+# fuse of two
+buildbop r1 -o b1 -t b2 -op fuse
+buildbop r2 -o b2 -t b3 -op fuse
+
+# fuse of all - it does not matter how the groups are formed
+buildbop r3 -o b1 b2 -t b3 -op fuse
+buildbop r4 -o b2 -t b1 b3 -op fuse
+buildbop r5 -o b1 b2 b3 -op fuse
+buildbop r6 -t b1 b2 b3 -op fuse
+
+# common of two
+buildbop r7 -o b2 -t b1 -op common
+buildbop r8 -o b1 -t b3 -op common
+
+# common
+buildbop r9 -o b1 -t b2 b3 -op common
+
+# cut
+buildbop r10 -o b1 -t b2 b3 -op cut
+
+# opposite cut
+buildbop r11 -o b1 -t b2 b3 -op tuc
+~~~~
+
 @subsubsection occt_draw_bop_build_CB Cells Builder
 
 See the @ref occt_algorithms_10c_Cells_1 "Cells Builder Usage" for the Draw usage of Cells Builder algorithm.
index fa9a69f..4e5bd1c 100644 (file)
@@ -6,94 +6,97 @@
 N/A
 
 .BOPAlgo_AlertBOPIsNotSet
-Error: The type of Boolean Operation is not set
+The type of Boolean Operation is not set
 
 .BOPAlgo_AlertBOPNotAllowed
-Error: Boolean operation of the given type is not allowed on the given inputs
+Boolean operation of the given type is not allowed on the given inputs
 
 .BOPAlgo_AlertSolidBuilderFailed
-Error: Building Fused solid has failed
+Building Fused solid has failed
 
 .BOPAlgo_AlertTooFewArguments
-Error: There are no enough arguments to perform the operation
+There are no enough arguments to perform the operation
 
 .BOPAlgo_AlertMultipleArguments
-Error: More than one argument is provided
+More than one argument is provided
 
 .BOPAlgo_AlertNoFiller
-Error: The Pave Filler (the intersection tool) has not been created
+The Pave Filler (the intersection tool) has not been created
 
 .BOPAlgo_AlertIntersectionFailed
-Error: The intersection of the arguments has failed
+The intersection of the arguments has failed
 
 .BOPAlgo_AlertBuilderFailed
-Error: Building of the result shape has failed
+Building of the result shape has failed
 
 .BOPAlgo_AlertNullFace
-Error: The face given to be split is a null shape
+The face given to be split is a null shape
 
 .BOPAlgo_AlertNullInputShapes
-Error: No or null input shapes
+No or null input shapes
 
 .BOPAlgo_AlertPostTreatFF
-Error: Cannot connect face intersection curves
+Cannot connect face intersection curves
 
 .BOPAlgo_AlertEmptyShape
-Warning: Some of the arguments are empty shapes
+Some of the arguments are empty shapes
 
 .BOPAlgo_AlertSelfInterferingShape
-Warning: Some of the arguments are self-interfering shapes
+Some of the arguments are self-interfering shapes
 
 .BOPAlgo_AlertTooSmallEdge
-Warning: Some edges are too small and have no valid range
+Some edges are too small and have no valid range
 
 .BOPAlgo_AlertNotSplittableEdge
-Warning: Some edges are very small and have such a small valid range, that they cannot be split
+Some edges are very small and have such a small valid range, that they cannot be split
 
 .BOPAlgo_AlertBadPositioning
-Warning: The positioning of the shapes leads to creation of the small edges without valid range
+The positioning of the shapes leads to creation of the small edges without valid range
 
 .BOPAlgo_AlertShellSplitterFailed
-Warning: Unable to build loops from the given faces
+Unable to build loops from the given faces
 
 .BOPAlgo_AlertRemovalOfIBForMDimShapes
-Warning: Removal of internal boundaries among the multi-dimensional shapes is not supported yet
+Removal of internal boundaries among the multi-dimensional shapes is not supported yet
 
 .BOPAlgo_AlertRemovalOfIBForSolidsFailed
-Warning: Removal of internal boundaries among Solids has failed
+Removal of internal boundaries among Solids has failed
 
 .BOPAlgo_AlertRemovalOfIBForFacesFailed
-Warning: Removal of internal boundaries among Faces has failed
+Removal of internal boundaries among Faces has failed
 
 .BOPAlgo_AlertRemovalOfIBForEdgesFailed
-Warning: Removal of internal boundaries among Edges has failed
+Removal of internal boundaries among Edges has failed
 
 .BOPAlgo_AlertIntersectionOfPairOfShapesFailed
-Warning: Intersection of pair of shapes has failed
+Intersection of pair of shapes has failed
 
 .BOPAlgo_AlertBuildingPCurveFailed
-Warning: Building 2D curve of edge on face has failed
+Building 2D curve of edge on face has failed
 
 .BOPAlgo_AlertAcquiredSelfIntersection
-Warning: Some sub-shapes of some of the argument become connected through other shapes and the argument became self-interfered
+Some sub-shapes of some of the argument become connected through other shapes and the argument became self-interfered
 
 .BOPAlgo_AlertUnsupportedType
-Warning: Unsupported type of input shape
+Unsupported type of input shape
 
 .BOPAlgo_AlertUnableToRemoveTheFeature
-Warning: Unable to remove the feature
+Unable to remove the feature
 
 .BOPAlgo_AlertNoFacesToRemove
-Error: No faces have been found for removal
+No faces have been found for removal
 
 .BOPAlgo_AlertRemoveFeaturesFailed
-Error: The Feature Removal algorithm has failed
+The Feature Removal algorithm has failed
 
 .BOPAlgo_AlertSolidBuilderUnusedFaces
-Warning: Some of the faces passed to the Solid Builder algorithm have not been classified and not used for solids creation
+Some of the faces passed to the Solid Builder algorithm have not been classified and not used for solids creation
 
 .BOPAlgo_AlertFaceBuilderUnusedEdges
-Warning: Some of the edges passed to the Face Builder algorithm have not been classified and not used for faces creation
+Some of the edges passed to the Face Builder algorithm have not been classified and not used for faces creation
 
 .BOPAlgo_AlertUnableToOrientTheShape
-Warning: Unable to orient the shape correctly
+Unable to orient the shape correctly
+
+.BOPAlgo_AlertUnknownShape
+Shape is unknown for operation
index 894f978..8274528 100644 (file)
@@ -111,4 +111,7 @@ DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertFaceBuilderUnusedEdges)
 //! Unable to orient the shape correctly
 DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToOrientTheShape)
 
+//! Shape is unknown for operation
+DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnknownShape)
+
 #endif // _BOPAlgo_Alerts_HeaderFile
index 8a134ca..bffec87 100644 (file)
@@ -30,6 +30,7 @@
 #include <TopAbs_ShapeEnum.hxx>
 #include <TopExp.hxx>
 #include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
 #include <TopoDS_Compound.hxx>
 #include <TopoDS_Edge.hxx>
 #include <TopoDS_Iterator.hxx>
@@ -772,6 +773,32 @@ void BOPAlgo_BOP::BuildRC()
 //=======================================================================
 void BOPAlgo_BOP::BuildShape()
 {
+  if (myDims[0] == 3 && myDims[1] == 3)
+  {
+    // For the Boolean operation on solids we need to check first
+    // if we are dealing with closed solids, because for open solids
+    // we cannot expect the BuilderSolid algorithm to produce good
+    // splits for them and have to try the alternative approach for
+    // building the result shape.
+    // This approach is not used by default as it will loose the
+    // modification history for solids, because the result solid
+    // will be built from scratch using the splits of faces.
+    Standard_Boolean hasNotClosedSolids = CheckArgsForOpenSolid();
+    if (hasNotClosedSolids)
+    {
+      Handle(Message_Report) aReport = new Message_Report();
+      BuildBOP(myArguments, myTools, myOperation, aReport);
+      if (aReport->GetAlerts(Message_Fail).IsEmpty())
+      {
+        // Success. Merge the report into the main report.
+        myReport->Merge(aReport);
+        return;
+      }
+    }
+  }
+
+  // Build the result using splits of arguments.
+
   BuildRC();
   //
   if ((myOperation == BOPAlgo_FUSE) && (myDims[0] == 3)) {
@@ -1169,6 +1196,157 @@ void BOPAlgo_BOP::BuildSolid()
   //
   myShape = aResult;
 }
+
+//=======================================================================
+//function : CheckArgsForOpenSolid
+//purpose  : 
+//=======================================================================
+Standard_Boolean BOPAlgo_BOP::CheckArgsForOpenSolid()
+{
+  // Analyze the report to find if BuilderSolid has generated warnings
+  // for any of the solids and collect these solids to check if they are open.
+  TopTools_MapOfShape aFailedSolids;
+  {
+    const Message_ListOfAlert& aList = myReport->GetAlerts(Message_Warning);
+    for (Message_ListOfAlert::Iterator aIt(aList); aIt.More(); aIt.Next())
+    {
+      const Handle(Standard_Type)& aType = aIt.Value()->DynamicType();
+      if (aType != STANDARD_TYPE(BOPAlgo_AlertSolidBuilderUnusedFaces))
+        continue;
+
+      Handle(TopoDS_AlertWithShape) aShapeAlert = Handle(TopoDS_AlertWithShape)::DownCast(aIt.Value());
+      if (!aShapeAlert.IsNull())
+      {
+        const TopoDS_Shape& aWarnShape = aShapeAlert->GetShape();
+        if (!aWarnShape.IsNull())
+        {
+          TopExp_Explorer expS(aWarnShape, TopAbs_SOLID);
+          for (; expS.More(); expS.Next())
+            aFailedSolids.Add(expS.Current());
+        }
+      }
+    }
+  }
+
+  // Iterate on all solids from the arguments and check if any
+  // of them are not closed.
+  // At the same time, collect all internal faces of the input solids
+  // to check if the splits of open solids did not acquire any new
+  // internal faces.
+
+  const Standard_Integer aNbS = myDS->NbSourceShapes();
+  for (Standard_Integer i = 0; i < aNbS; ++i)
+  {
+    const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(i);
+    if (aSI.ShapeType() != TopAbs_SOLID)
+      continue;
+
+    const TopoDS_Shape& aSolid = aSI.Shape();
+
+    // Check that not INTERNAL faces create closed loops
+    TopTools_IndexedDataMapOfShapeListOfShape aMEF;
+    // Collect all splits of internal faces
+    TopTools_MapOfShape aMFInternal;
+
+    for (TopoDS_Iterator itSh(aSolid); itSh.More(); itSh.Next())
+    {
+      const TopoDS_Shape& aSh = itSh.Value();
+      if (aSh.ShapeType() != TopAbs_SHELL)
+        continue;
+
+      for (TopoDS_Iterator itF(aSh); itF.More(); itF.Next())
+      {
+        const TopoDS_Shape& aF = itF.Value();
+        if (aF.Orientation() == TopAbs_INTERNAL)
+        {
+          const TopTools_ListOfShape* pLFIm = myImages.Seek(aF);
+          if (pLFIm)
+          {
+            TopTools_ListOfShape::Iterator itLFIm(*pLFIm);
+            for (; itLFIm.More(); itLFIm.Next())
+              aMFInternal.Add(itLFIm.Value());
+          }
+          else
+            aMFInternal.Add(aF);
+        }
+        else
+          TopExp::MapShapesAndAncestors(aF, TopAbs_EDGE, TopAbs_FACE, aMEF);
+      }
+    }
+
+    // Analyze the Edge-Face connection map on free edges
+    Standard_Boolean isClosed = Standard_True;
+    const Standard_Integer aNbE = aMEF.Extent();
+    for (Standard_Integer j = 1; j <= aNbE && isClosed; ++j)
+    {
+      const TopoDS_Edge& aE = TopoDS::Edge(aMEF.FindKey(j));
+      if (BRep_Tool::Degenerated(aE))
+        // Skip degenerated edges
+        continue;
+
+      isClosed = (aMEF(j).Extent() > 1);
+      if (!isClosed)
+      {
+        const TopoDS_Face& aF = TopoDS::Face(aMEF(j).First());
+        isClosed = BRep_Tool::IsClosed(aE, aF); // Check for seam edges
+        if (!isClosed)
+        {
+          // Check if the edge is not internal in the face
+          TopExp_Explorer expE(aF, TopAbs_EDGE);
+          for (; expE.More(); expE.Next())
+          {
+            if (expE.Current().IsSame(aE))
+            {
+              isClosed = (expE.Current().Orientation() == TopAbs_INTERNAL);
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    if (isClosed)
+      continue;
+
+    // Not closed solid is found
+
+    if (aFailedSolids.Contains(aSolid))
+      // Warning has been generated for this solid, return positive result right away.
+      return Standard_True;
+
+    
+    // Check the splits not to acquire new INTERNAL faces
+    const TopTools_ListOfShape *pLSIm = myImages.Seek(aSolid);
+    if (!pLSIm)
+      continue;
+
+    TopTools_ListOfShape::Iterator itLSIm(*pLSIm);
+    for (; itLSIm.More(); itLSIm.Next())
+    {
+      const TopoDS_Shape& aSIm = itLSIm.Value();
+      for (TopoDS_Iterator itSh(aSIm); itSh.More(); itSh.Next())
+      {
+        const TopoDS_Shape& aSh = itSh.Value();
+        if (aSh.ShapeType() != TopAbs_SHELL)
+          continue;
+
+        for (TopoDS_Iterator itF(aSh); itF.More(); itF.Next())
+        {
+          const TopoDS_Shape& aF = itF.Value();
+          if (aF.Orientation() == TopAbs_INTERNAL)
+          {
+            if (!aMFInternal.Contains(aF))
+              // New internal face is found
+              return Standard_True;
+          }
+        }
+      }
+    }
+  }
+
+  return Standard_False;
+}
+
 //=======================================================================
 //function : TypeToExplore
 //purpose  : 
index 8f18135..4598262 100644 (file)
@@ -107,6 +107,12 @@ protected:
   //! all shapes in one of the groups are empty shapes.
   Standard_EXPORT Standard_Boolean TreatEmptyShape();
 
+  //! Checks if the arguments of Boolean Operation on solids
+  //! contain any open solids, for which the building of the splits
+  //! has failed. In case of positive check, run different procedure
+  //! for building the result shape.
+  Standard_EXPORT virtual Standard_Boolean CheckArgsForOpenSolid();
+
 protected:
 
   BOPAlgo_Operation myOperation;
index f13a73f..61ea072 100644 (file)
@@ -9,94 +9,97 @@ static const char BOPAlgo_BOPAlgo_msg[] =
   "N/A\n"
   "\n"
   ".BOPAlgo_AlertBOPIsNotSet\n"
-  "Error: The type of Boolean Operation is not set\n"
+  "The type of Boolean Operation is not set\n"
   "\n"
   ".BOPAlgo_AlertBOPNotAllowed\n"
-  "Error: Boolean operation of the given type is not allowed on the given inputs\n"
+  "Boolean operation of the given type is not allowed on the given inputs\n"
   "\n"
   ".BOPAlgo_AlertSolidBuilderFailed\n"
-  "Error: Building Fused solid has failed\n"
+  "Building Fused solid has failed\n"
   "\n"
   ".BOPAlgo_AlertTooFewArguments\n"
-  "Error: There are no enough arguments to perform the operation\n"
+  "There are no enough arguments to perform the operation\n"
   "\n"
   ".BOPAlgo_AlertMultipleArguments\n"
-  "Error: More than one argument is provided\n"
+  "More than one argument is provided\n"
   "\n"
   ".BOPAlgo_AlertNoFiller\n"
-  "Error: The Pave Filler (the intersection tool) has not been created\n"
+  "The Pave Filler (the intersection tool) has not been created\n"
   "\n"
   ".BOPAlgo_AlertIntersectionFailed\n"
-  "Error: The intersection of the arguments has failed\n"
+  "The intersection of the arguments has failed\n"
   "\n"
   ".BOPAlgo_AlertBuilderFailed\n"
-  "Error: Building of the result shape has failed\n"
+  "Building of the result shape has failed\n"
   "\n"
   ".BOPAlgo_AlertNullFace\n"
-  "Error: The face given to be split is a null shape\n"
+  "The face given to be split is a null shape\n"
   "\n"
   ".BOPAlgo_AlertNullInputShapes\n"
-  "Error: No or null input shapes\n"
+  "No or null input shapes\n"
   "\n"
   ".BOPAlgo_AlertPostTreatFF\n"
-  "Error: Cannot connect face intersection curves\n"
+  "Cannot connect face intersection curves\n"
   "\n"
   ".BOPAlgo_AlertEmptyShape\n"
-  "Warning: Some of the arguments are empty shapes\n"
+  "Some of the arguments are empty shapes\n"
   "\n"
   ".BOPAlgo_AlertSelfInterferingShape\n"
-  "Warning: Some of the arguments are self-interfering shapes\n"
+  "Some of the arguments are self-interfering shapes\n"
   "\n"
   ".BOPAlgo_AlertTooSmallEdge\n"
-  "Warning: Some edges are too small and have no valid range\n"
+  "Some edges are too small and have no valid range\n"
   "\n"
   ".BOPAlgo_AlertNotSplittableEdge\n"
-  "Warning: Some edges are very small and have such a small valid range, that they cannot be split\n"
+  "Some edges are very small and have such a small valid range, that they cannot be split\n"
   "\n"
   ".BOPAlgo_AlertBadPositioning\n"
-  "Warning: The positioning of the shapes leads to creation of the small edges without valid range\n"
+  "The positioning of the shapes leads to creation of the small edges without valid range\n"
   "\n"
   ".BOPAlgo_AlertShellSplitterFailed\n"
-  "Warning: Unable to build loops from the given faces\n"
+  "Unable to build loops from the given faces\n"
   "\n"
   ".BOPAlgo_AlertRemovalOfIBForMDimShapes\n"
-  "Warning: Removal of internal boundaries among the multi-dimensional shapes is not supported yet\n"
+  "Removal of internal boundaries among the multi-dimensional shapes is not supported yet\n"
   "\n"
   ".BOPAlgo_AlertRemovalOfIBForSolidsFailed\n"
-  "Warning: Removal of internal boundaries among Solids has failed\n"
+  "Removal of internal boundaries among Solids has failed\n"
   "\n"
   ".BOPAlgo_AlertRemovalOfIBForFacesFailed\n"
-  "Warning: Removal of internal boundaries among Faces has failed\n"
+  "Removal of internal boundaries among Faces has failed\n"
   "\n"
   ".BOPAlgo_AlertRemovalOfIBForEdgesFailed\n"
-  "Warning: Removal of internal boundaries among Edges has failed\n"
+  "Removal of internal boundaries among Edges has failed\n"
   "\n"
   ".BOPAlgo_AlertIntersectionOfPairOfShapesFailed\n"
-  "Warning: Intersection of pair of shapes has failed\n"
+  "Intersection of pair of shapes has failed\n"
   "\n"
   ".BOPAlgo_AlertBuildingPCurveFailed\n"
-  "Warning: Building 2D curve of edge on face has failed\n"
+  "Building 2D curve of edge on face has failed\n"
   "\n"
   ".BOPAlgo_AlertAcquiredSelfIntersection\n"
-  "Warning: Some sub-shapes of some of the argument become connected through other shapes and the argument became self-interfered\n"
+  "Some sub-shapes of some of the argument become connected through other shapes and the argument became self-interfered\n"
   "\n"
   ".BOPAlgo_AlertUnsupportedType\n"
-  "Warning: Unsupported type of input shape\n"
+  "Unsupported type of input shape\n"
   "\n"
   ".BOPAlgo_AlertUnableToRemoveTheFeature\n"
-  "Warning: Unable to remove the feature\n"
+  "Unable to remove the feature\n"
   "\n"
   ".BOPAlgo_AlertNoFacesToRemove\n"
-  "Error: No faces have been found for removal\n"
+  "No faces have been found for removal\n"
   "\n"
   ".BOPAlgo_AlertRemoveFeaturesFailed\n"
-  "Error: The Feature Removal algorithm has failed\n"
+  "The Feature Removal algorithm has failed\n"
   "\n"
   ".BOPAlgo_AlertSolidBuilderUnusedFaces\n"
-  "Warning: Some of the faces passed to the Solid Builder algorithm have not been classified and not used for solids creation\n"
+  "Some of the faces passed to the Solid Builder algorithm have not been classified and not used for solids creation\n"
   "\n"
   ".BOPAlgo_AlertFaceBuilderUnusedEdges\n"
-  "Warning: Some of the edges passed to the Face Builder algorithm have not been classified and not used for faces creation\n"
+  "Some of the edges passed to the Face Builder algorithm have not been classified and not used for faces creation\n"
   "\n"
   ".BOPAlgo_AlertUnableToOrientTheShape\n"
-  "Warning: Unable to orient the shape correctly\n";
+  "Unable to orient the shape correctly\n"
+  "\n"
+  ".BOPAlgo_AlertUnknownShape\n"
+  "Shape is unknown for operation\n";
index c986f6a..9c04e66 100644 (file)
 
 
 #include <BOPAlgo_Builder.hxx>
-#include <BOPAlgo_PaveFiller.hxx>
 #include <BOPAlgo_Alerts.hxx>
+#include <BOPAlgo_BuilderSolid.hxx>
+#include <BOPAlgo_PaveFiller.hxx>
+#include <BOPAlgo_Tools.hxx>
+#include <BOPDS_DS.hxx>
+#include <BOPDS_ShapeInfo.hxx>
 #include <BOPTools_AlgoTools.hxx>
 #include <BRep_Builder.hxx>
 #include <IntTools_Context.hxx>
 #include <Standard_ErrorHandler.hxx>
 #include <Standard_Failure.hxx>
+#include <TopExp.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
 #include <TopoDS_Compound.hxx>
-#include <BRep_Builder.hxx>
-
-#include <BOPDS_ShapeInfo.hxx>
-#include <BOPDS_DS.hxx>
-
-#include <BOPTools_AlgoTools.hxx>
+#include <TopoDS_Solid.hxx>
 #include <TopTools_IndexedMapOfShape.hxx>
-#include <TopTools_ListIteratorOfListOfShape.hxx>
+#include <TopTools_MapOfOrientedShape.hxx>
 
 
 //=======================================================================
@@ -50,6 +52,7 @@ BOPAlgo_Builder::BOPAlgo_Builder()
   myImages(100, myAllocator),
   myShapesSD(100, myAllocator),
   myOrigins(100, myAllocator),
+  myInParts(100, myAllocator),
   myNonDestructive(Standard_False),
   myGlue(BOPAlgo_GlueOff),
   myCheckInverted(Standard_True)
@@ -71,6 +74,7 @@ BOPAlgo_Builder::BOPAlgo_Builder
   myImages(100, myAllocator), 
   myShapesSD(100, myAllocator),
   myOrigins(100, myAllocator),
+  myInParts(100, myAllocator),
   myNonDestructive(Standard_False),
   myGlue(BOPAlgo_GlueOff),
   myCheckInverted(Standard_True)
@@ -101,6 +105,7 @@ void BOPAlgo_Builder::Clear()
   myImages.Clear();
   myShapesSD.Clear();
   myOrigins.Clear();
+  myInParts.Clear();
 }
 //=======================================================================
 //function : AddArgument
@@ -376,3 +381,366 @@ void BOPAlgo_Builder::PostTreat()
   BOPTools_AlgoTools::CorrectTolerances(myShape, aMA, 0.05, myRunParallel);
   BOPTools_AlgoTools::CorrectShapeTolerances(myShape, aMA, myRunParallel);
 }
+
+//=======================================================================
+//function : BuildBOP
+//purpose  : 
+//=======================================================================
+void BOPAlgo_Builder::BuildBOP(const TopTools_ListOfShape& theObjects,
+                               const TopAbs_State          theObjState,
+                               const TopTools_ListOfShape& theTools,
+                               const TopAbs_State          theToolsState,
+                               Handle(Message_Report)      theReport)
+{
+  if (HasErrors())
+    return;
+
+  // Report for the method
+  Handle(Message_Report) aReport = theReport.IsNull() ? myReport : theReport;
+
+  if (myArguments.IsEmpty() || myShape.IsNull())
+  {
+    aReport->AddAlert(Message_Fail, new BOPAlgo_AlertBuilderFailed());
+    return;
+  }
+
+  // Check the input data
+  if ((theObjState   != TopAbs_IN && theObjState   != TopAbs_OUT) ||
+      (theToolsState != TopAbs_IN && theToolsState != TopAbs_OUT))
+  {
+    aReport->AddAlert(Message_Fail, new BOPAlgo_AlertBOPNotSet());
+    return;
+  }
+
+  // Check input shapes
+  Standard_Boolean hasObjects = !theObjects.IsEmpty();
+  Standard_Boolean hasTools   = !theTools  .IsEmpty();
+  if (!hasObjects && !hasTools)
+  {
+    aReport->AddAlert(Message_Fail, new BOPAlgo_AlertTooFewArguments());
+    return;
+  }
+
+  // Check that all input solids are from the arguments
+  for (Standard_Integer i = 0; i < 2; ++i)
+  {
+    const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
+    TopTools_ListOfShape::Iterator itLS(aList);
+    for (; itLS.More(); itLS.Next())
+    {
+      const TopoDS_Shape& aS = itLS.Value();
+      // Check if the shape belongs to the arguments of operation
+      if (myDS->Index(aS) < 0)
+      {
+        aReport->AddAlert(Message_Fail, new BOPAlgo_AlertUnknownShape(aS));
+        return;
+      }
+
+      // Check if the shape is a solid or collection of them
+      if (aS.ShapeType() != TopAbs_SOLID)
+      {
+        TopTools_ListOfShape aLS;
+        TopTools_MapOfShape aMFence;
+        BOPAlgo_Tools::TreatCompound(aS, aMFence, aLS);
+
+        TopTools_ListOfShape::Iterator it(aLS);
+        for (; it.More(); it.Next())
+        {
+          const TopoDS_Shape& aSx = it.Value();
+          if (aSx.ShapeType() != TopAbs_SOLID &&
+              aSx.ShapeType() != TopAbs_COMPSOLID)
+          {
+            aReport->AddAlert(Message_Fail, new BOPAlgo_AlertUnsupportedType(aS));
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  // Classification of the faces relatively solids has been made
+  // on the stage of Solids splitting. All results are saved into
+  // myInParts map, which connects the solids with its IN faces from
+  // other arguments. All faces not contained in the list of IN faces
+  // will be considered as OUT.
+
+  // Prepare the maps of splits of solids faces with orientations
+  TopTools_IndexedMapOfOrientedShape aMObjFacesOri, aMToolFacesOri;
+  // Prepare the maps of splits of solids faces
+  TopTools_IndexedMapOfShape aMObjFaces, aMToolFaces;
+  // Copy the list of IN faces of the solids into map
+  TopTools_MapOfShape anINObjects, anINTools;
+
+  for (Standard_Integer i = 0; i < 2; ++i)
+  {
+    const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
+    TopTools_IndexedMapOfOrientedShape& aMapOri  = !i ? aMObjFacesOri : aMToolFacesOri;
+    TopTools_IndexedMapOfShape& aMap = !i ? aMObjFaces : aMToolFaces;
+    TopTools_ListOfShape::Iterator itLS(aList);
+    for (; itLS.More(); itLS.Next())
+    {
+      const TopoDS_Shape& aShape = itLS.Value();
+      TopExp_Explorer expS(aShape, TopAbs_SOLID);
+      for (; expS.More(); expS.Next())
+      {
+        const TopoDS_Shape& aS = expS.Current();
+        TopExp_Explorer expF(aS, TopAbs_FACE);
+        for (; expF.More(); expF.Next())
+        {
+          const TopoDS_Shape& aF = expF.Current();
+          if (aF.Orientation() != TopAbs_FORWARD &&
+              aF.Orientation() != TopAbs_REVERSED)
+            continue;
+          const TopTools_ListOfShape* pLFIm = myImages.Seek(aF);
+          if (pLFIm)
+          {
+            TopTools_ListOfShape::Iterator itLFIm(*pLFIm);
+            for (; itLFIm.More(); itLFIm.Next())
+            {
+              TopoDS_Face aFIm = TopoDS::Face(itLFIm.Value());
+              if (BOPTools_AlgoTools::IsSplitToReverse(aFIm, aF, myContext))
+                aFIm.Reverse();
+              aMapOri.Add(aFIm);
+              aMap.Add(aFIm);
+            }
+          }
+          else
+          {
+            aMapOri.Add(aF);
+            aMap.Add(aF);
+          }
+        }
+
+        // Copy the list of IN faces into a map
+        const TopTools_ListOfShape* pLFIN = myInParts.Seek(aS);
+        if (pLFIN)
+        {
+          TopTools_MapOfShape& anINMap = !i ? anINObjects : anINTools;
+          TopTools_ListOfShape::Iterator itLFIn(*pLFIN);
+          for (; itLFIn.More(); itLFIn.Next())
+            anINMap.Add(itLFIn.Value());
+        }
+      }
+    }
+  }
+
+  // Now we need to select all faces which will participate in
+  // building of the resulting solids. The final set of faces
+  // depends on the given states for the groups.
+  Standard_Boolean isObjectsIN = (theObjState   == TopAbs_IN),
+                   isToolsIN   = (theToolsState == TopAbs_IN);
+
+  // Shortcuts
+  Standard_Boolean bAvoidIN = (!isObjectsIN && !isToolsIN), // avoid all in faces
+                   bAvoidINforBoth = (isObjectsIN != isToolsIN); // avoid faces IN for both groups
+
+  // Choose which SD faces are needed to be taken - equally or differently oriented faces
+  Standard_Boolean isSameOriNeeded = (theObjState == theToolsState);
+  // Resulting faces
+  TopTools_IndexedMapOfOrientedShape aMResFacesOri;
+  TopTools_MapOfShape aMResFacesFence;
+  // Fence map
+  TopTools_MapOfShape aMFence, aMFToAvoid;
+  // Oriented fence map
+  TopTools_MapOfOrientedShape aMFenceOri;
+
+  for (Standard_Integer i = 0; i < 2; ++i)
+  {
+    const TopTools_IndexedMapOfOrientedShape& aMap  = !i ? aMObjFacesOri : aMToolFacesOri;
+    const TopTools_IndexedMapOfShape& anOppositeMap  = !i ? aMToolFaces : aMObjFaces;
+    const TopTools_MapOfShape& anINMap = !i ? anINObjects : anINTools;
+    const TopTools_MapOfShape& anOppositeINMap = !i ? anINTools : anINObjects;
+    const Standard_Boolean bTakeIN = !i ? isObjectsIN : isToolsIN;
+
+    const Standard_Integer aNbF = aMap.Extent();
+    for (Standard_Integer j = 1; j <= aNbF; ++j)
+    {
+      TopoDS_Shape aFIm = aMap(j);
+
+      Standard_Boolean isIN = anINMap.Contains(aFIm);
+      Standard_Boolean isINOpposite = anOppositeINMap.Contains(aFIm);
+
+      // Filtering for FUSE - avoid any IN faces
+      if (bAvoidIN && (isIN || isINOpposite))
+        continue;
+
+      // Filtering for CUT - avoid faces IN for both groups
+      if (bAvoidINforBoth && isIN && isINOpposite)
+        continue;
+
+      // Treatment of SD faces
+      if (!aMFence.Add(aFIm))
+      {
+        if (!anOppositeMap.Contains(aFIm))
+        {
+          // The face belongs to only one group
+          if (bTakeIN != isSameOriNeeded)
+            aMFToAvoid.Add(aFIm);
+        }
+        else
+        {
+          // The face belongs to both groups.
+          // Using its orientation decide if it is needed in the result or not.
+          Standard_Boolean isSameOri = !aMFenceOri.Add(aFIm);
+          if (isSameOriNeeded == isSameOri)
+          {
+            // Take the shape without classification
+            if (aMResFacesFence.Add(aFIm))
+              aMResFacesOri.Add(aFIm);
+          }
+          else
+            // Remove the face
+            aMFToAvoid.Add(aFIm);
+
+          continue;
+        }
+      }
+      if (!aMFenceOri.Add(aFIm))
+        continue;
+
+      if (bTakeIN == isINOpposite)
+      {
+        if (isIN)
+        {
+          aMResFacesOri.Add(aFIm);
+          aMResFacesOri.Add(aFIm.Reversed());
+        }
+        else if (bTakeIN && !isSameOriNeeded)
+          aMResFacesOri.Add(aFIm.Reversed());
+        else
+          aMResFacesOri.Add(aFIm);
+        aMResFacesFence.Add(aFIm);
+      }
+    }
+  }
+
+  // Remove the faces which has to be avoided
+  TopTools_ListOfShape aResFaces;
+  const Standard_Integer aNbRF = aMResFacesOri.Extent();
+  for (Standard_Integer i = 1; i <= aNbRF; ++i)
+  {
+    const TopoDS_Shape& aRF = aMResFacesOri(i);
+    if (!aMFToAvoid.Contains(aRF))
+      aResFaces.Append(aRF);
+  }
+
+  BRep_Builder aBB;
+
+  // Try to build closed solids from the faces
+  BOPAlgo_BuilderSolid aBS;
+  aBS.SetShapes(aResFaces);
+  aBS.SetRunParallel(myRunParallel);
+  aBS.SetContext(myContext);
+  aBS.SetFuzzyValue(myFuzzyValue);
+  aBS.SetProgressIndicator(myProgressIndicator);
+  aBS.Perform();
+
+  // Resulting solids
+  TopTools_ListOfShape aResSolids;
+
+  aMFence.Clear();
+  if (!aBS.HasErrors())
+  {
+    // If any, add solids into resulting compound
+    TopTools_ListIteratorOfListOfShape itA(aBS.Areas());
+    for (; itA.More(); itA.Next())
+    {
+      const TopoDS_Shape& aSolid = itA.Value();
+      // The solid must contain at least one face
+      // from either of objects or tools
+      TopExp_Explorer expF(aSolid, TopAbs_FACE);
+      for (; expF.More(); expF.Next())
+      {
+        const TopoDS_Shape& aF = expF.Current();
+        if (aMObjFacesOri.Contains(aF) || aMToolFacesOri.Contains(aF))
+          break;
+      }
+      if (expF.More())
+      {
+        aResSolids.Append(aSolid);
+        TopExp::MapShapes(aSolid, aMFence);
+      }
+    }
+  }
+
+  // Collect unused faces
+  TopoDS_Compound anUnUsedFaces;
+  aBB.MakeCompound(anUnUsedFaces);
+
+  TopTools_ListOfShape::Iterator itLF(aResFaces);
+  for (; itLF.More(); itLF.Next())
+  {
+    if (aMFence.Add(itLF.Value()))
+      aBB.Add(anUnUsedFaces, itLF.Value());
+  }
+
+  // Build blocks from the unused faces
+  TopTools_ListOfShape aLCB;
+  BOPTools_AlgoTools::MakeConnexityBlocks(anUnUsedFaces, TopAbs_EDGE, TopAbs_FACE, aLCB);
+
+  // Build solid from each block
+  TopTools_ListIteratorOfListOfShape itCB(aLCB);
+  for (; itCB.More(); itCB.Next())
+  {
+    const TopoDS_Shape& aCB = itCB.Value();
+    TopoDS_Shell aShell;
+    aBB.MakeShell(aShell);
+    // Add faces of the block to the shell
+    TopExp_Explorer anExpF(aCB, TopAbs_FACE);
+    for (; anExpF.More(); anExpF.Next())
+      aBB.Add(aShell, TopoDS::Face(anExpF.Current()));
+
+    BOPTools_AlgoTools::OrientFacesOnShell(aShell);
+    // Make solid out of the shell
+    TopoDS_Solid aSolid;
+    aBB.MakeSolid(aSolid);
+    aBB.Add(aSolid, aShell);
+    // Add new solid to result
+    aResSolids.Append(aSolid);
+  }
+
+  if (!bAvoidIN)
+  {
+    // Fill solids with internal parts coming with the solids
+    TopTools_ListOfShape anInParts;
+    for (Standard_Integer i = 0; i < 2; ++i)
+    {
+      const TopTools_ListOfShape& aList = !i ? theObjects : theTools;
+      TopTools_ListOfShape::Iterator itLS(aList);
+      for (; itLS.More(); itLS.Next())
+      {
+        TopExp_Explorer expS(itLS.Value(), TopAbs_SOLID);
+        for (; expS.More(); expS.Next())
+        {
+          const TopoDS_Shape& aS = expS.Current(); // Solid
+          for (TopoDS_Iterator it(aS); it.More(); it.Next())
+          {
+            const TopoDS_Shape& aSInt = it.Value();
+            if (aSInt.Orientation() == TopAbs_INTERNAL)
+              anInParts.Append(aSInt); // vertex or edge
+            else
+            {
+              // shell treatment
+              TopoDS_Iterator itInt(aSInt);
+              if (itInt.More() && itInt.Value().Orientation() == TopAbs_INTERNAL)
+                anInParts.Append(aSInt);
+            }
+          }
+        }
+      }
+    }
+
+    BOPAlgo_Tools::FillInternals(aResSolids, anInParts, myImages, myContext);
+  }
+
+  // Combine solids into compound
+  TopoDS_Shape aResult;
+  aBB.MakeCompound(TopoDS::Compound(aResult));
+
+  TopTools_ListOfShape::Iterator itLS(aResSolids);
+  for (; itLS.More(); itLS.Next())
+    aBB.Add(aResult, itLS.Value());
+
+  myShape = aResult;
+  PrepareHistory();
+}
index c9bdc77..fb121ec 100644 (file)
@@ -25,6 +25,7 @@
 #include <BOPAlgo_PPaveFiller.hxx>
 #include <BOPAlgo_BuilderShape.hxx>
 #include <BOPAlgo_GlueEnum.hxx>
+#include <BOPAlgo_Operation.hxx>
 #include <BOPDS_PDS.hxx>
 #include <NCollection_BaseAllocator.hxx>
 #include <Standard_Integer.hxx>
@@ -38,6 +39,7 @@
 class IntTools_Context;
 class TopoDS_Shape;
 class BOPAlgo_PaveFiller;
+class TopoDS_Solid;
 
 //!
 //! The class is a General Fuse algorithm - base algorithm for the
@@ -174,6 +176,114 @@ public: //! @name Performing the operation
   Standard_EXPORT virtual void PerformWithFiller (const BOPAlgo_PaveFiller& theFiller);
 
 
+public: //! @name BOPs on open solids
+
+  //! Builds the result shape according to the given states for the objects
+  //! and tools. These states can be unambiguously converted into the Boolean operation type.
+  //! Thus, it performs the Boolean operation on the given groups of shapes.
+  //!
+  //! The result is built basing on the result of Builder operation (GF or any other).
+  //! The only condition for the Builder is that the splits of faces should be created
+  //! and classified relatively solids.
+  //!
+  //! The method uses classification approach for choosing the faces which will
+  //! participate in building the result shape:
+  //! - All faces from each group having the given state for the opposite group
+  //!   will be taken into result.
+  //!
+  //! Such approach shows better results (in comparison with BOPAlgo_BuilderSolid approach)
+  //! when working with open solids. However, the result may not be always
+  //! correct on such data (at least, not as expected) as the correct classification
+  //! of the faces relatively open solids is not always possible and may vary
+  //! depending on the chosen classification point on the face.
+  //!
+  //! History is not created for the solids in this method.
+  //!
+  //! To avoid pollution of the report of Builder algorithm, there is a possibility to pass
+  //! the different report to collect the alerts of the method only. But, if the new report
+  //! is not given, the Builder report will be used.
+  //! So, even if Builder passed without any errors, but some error has been stored into its report
+  //! in this method, for the following calls the Builder report must be cleared.
+  //!
+  //! The method may set the following errors:
+  //! - BOPAlgo_AlertBuilderFailed - Building operation has not been performed yet or failed;
+  //! - BOPAlgo_AlertBOPNotSet - invalid BOP type is given (COMMON/FUSE/CUT/CUT21 are supported);
+  //! - BOPAlgo_AlertTooFewArguments - arguments are not given;
+  //! - BOPAlgo_AlertUnknownShape - the shape is unknown for the operation.
+  //!
+  //! Parameters:
+  //! @param theObjects   - The group of Objects for BOP;
+  //! @param theObjState  - State for objects faces to pass into result;
+  //! @param theTools     - The group of Tools for BOP;
+  //! @param theObjState  - State for tools faces to pass into result;
+  //! @param theReport    - The alternative report to avoid pollution of the main one.
+  Standard_EXPORT virtual void BuildBOP(const TopTools_ListOfShape& theObjects,
+                                        const TopAbs_State          theObjState,
+                                        const TopTools_ListOfShape& theTools,
+                                        const TopAbs_State          theToolsState,
+                                        Handle(Message_Report)      theReport = NULL);
+
+  //! Builds the result of Boolean operation of given type
+  //! basing on the result of Builder operation (GF or any other).
+  //!
+  //! The method converts the given type of operation into the states
+  //! for the objects and tools required for their face to pass into result
+  //! and performs the call to the same method, but with states instead
+  //! of operation type.
+  //!
+  //! The conversion looks as follows:
+  //! - COMMON is built from the faces of objects located IN any of the tools
+  //!          and vice versa.
+  //! - FUSE   is built from the faces OUT of all given shapes;
+  //! - CUT    is built from the faces of the objects OUT of the tools and
+  //!          faces of the tools located IN solids of the objects.
+  //!
+  //! @param theObjects   - The group of Objects for BOP;
+  //! @param theTools     - The group of Tools for BOP;
+  //! @param theOperation - The BOP type;
+  //! @param theReport    - The alternative report to avoid pollution of the global one.
+  void BuildBOP(const TopTools_ListOfShape& theObjects,
+                const TopTools_ListOfShape& theTools,
+                const BOPAlgo_Operation     theOperation,
+                Handle(Message_Report)      theReport = NULL)
+  {
+    TopAbs_State anObjState, aToolsState;
+    switch (theOperation)
+    {
+      case BOPAlgo_COMMON:
+      {
+        anObjState  = TopAbs_IN;
+        aToolsState = TopAbs_IN;
+        break;
+      }
+      case BOPAlgo_FUSE:
+      {
+        anObjState  = TopAbs_OUT;
+        aToolsState = TopAbs_OUT;
+        break;
+      }
+      case BOPAlgo_CUT:
+      {
+        anObjState  = TopAbs_OUT;
+        aToolsState = TopAbs_IN;
+        break;
+      }
+      case BOPAlgo_CUT21:
+      {
+        anObjState  = TopAbs_IN;
+        aToolsState = TopAbs_OUT;
+        break;
+      }
+      default:
+      {
+        anObjState  = TopAbs_UNKNOWN;
+        aToolsState = TopAbs_UNKNOWN;
+        break;
+      }
+    }
+    BuildBOP(theObjects, anObjState, theTools, aToolsState, theReport);
+  }
+
 protected: //! @name History methods
 
   //! Prepare information for history support.
@@ -315,15 +425,11 @@ protected: //! @name Fill Images of SOLIDS
                                         TopTools_ListOfShape& theLIF);
 
   //! Finds faces located inside each solid.
-  Standard_EXPORT virtual void FillIn3DParts (TopTools_DataMapOfShapeListOfShape& theInParts,
-                                              TopTools_DataMapOfShapeShape& theDraftSolids,
-                                              const Handle(NCollection_BaseAllocator)& theAllocator);
+  Standard_EXPORT virtual void FillIn3DParts(TopTools_DataMapOfShapeShape& theDraftSolids);
 
   //! Builds the splits of the solids using their draft versions
   //! and faces located inside.
-  Standard_EXPORT void BuildSplitSolids (TopTools_DataMapOfShapeListOfShape& theInParts,
-                                         TopTools_DataMapOfShapeShape& theDraftSolids,
-                                         const Handle(NCollection_BaseAllocator)& theAllocator);
+  Standard_EXPORT void BuildSplitSolids(TopTools_DataMapOfShapeShape& theDraftSolids);
 
   //! Classifies the vertices and edges from the arguments relatively
   //! splits of solids and makes them INTERNAL for solids.
@@ -358,6 +464,7 @@ protected: //! @name Fields
   TopTools_DataMapOfShapeListOfShape myImages;  //!< Images - map of Images of the sub-shapes of arguments
   TopTools_DataMapOfShapeShape myShapesSD;      //!< ShapesSD - map of SD Shapes
   TopTools_DataMapOfShapeListOfShape myOrigins; //!< Origins - map of Origins, back map of Images
+  TopTools_DataMapOfShapeListOfShape myInParts; //!< InParts - map of own and acquired IN faces of the arguments solids
   Standard_Boolean myNonDestructive;            //!< Safe processing option allows avoiding modification of the input shapes
   BOPAlgo_GlueEnum myGlue;                      //!< Gluing option allows speeding up the intersection of the input shapes
   Standard_Boolean myCheckInverted;             //!< Check inverted option allows disabling the check of input solids on inverted status
index 942b92b..2896c4c 100644 (file)
@@ -23,6 +23,7 @@
 #include <TopAbs_State.hxx>
 //
 #include <TopoDS.hxx>
+#include <TopoDS_AlertWithShape.hxx>
 #include <TopoDS_Iterator.hxx>
 #include <TopoDS_Solid.hxx>
 #include <TopoDS_Shape.hxx>
@@ -90,29 +91,21 @@ void BOPAlgo_Builder::FillImagesSolids()
   if (!bHasSolids) {
     return;
   }
-  // 
-  Handle(NCollection_BaseAllocator) aAlr;
-  //
-  aAlr=NCollection_BaseAllocator::CommonBaseAllocator();
-  //
-  TopTools_DataMapOfShapeListOfShape theInParts(100, aAlr);
-  TopTools_DataMapOfShapeShape theDraftSolids(100, aAlr);
-  //
-  FillIn3DParts(theInParts, theDraftSolids, aAlr); 
-  BuildSplitSolids(theInParts, theDraftSolids, aAlr);
+
+  // Draft solids
+  TopTools_DataMapOfShapeShape aDraftSolids;
+  // Find all IN faces for all IN faces
+  FillIn3DParts(aDraftSolids);
+  // Build split of the solids
+  BuildSplitSolids(aDraftSolids);
+  // Fill solids with internal parts
   FillInternalShapes();
-  //
-  theInParts.Clear();
-  theDraftSolids.Clear();
 }
 //=======================================================================
 //function : FillIn3DParts
 //purpose  : 
 //=======================================================================
-void BOPAlgo_Builder::FillIn3DParts
-  (TopTools_DataMapOfShapeListOfShape& theInParts,
-   TopTools_DataMapOfShapeShape& theDraftSolids,
-   const Handle(NCollection_BaseAllocator)& )
+void BOPAlgo_Builder::FillIn3DParts(TopTools_DataMapOfShapeShape& theDraftSolids)
 {
   Handle(NCollection_BaseAllocator) anAlloc = new NCollection_IncAllocator;
 
@@ -224,7 +217,7 @@ void BOPAlgo_Builder::FillIn3DParts
     if (aNbInt || aNbIN)
     {
       // Combine the lists
-      TopTools_ListOfShape *pLIN  = theInParts.Bound(aSolid, TopTools_ListOfShape());
+      TopTools_ListOfShape *pLIN  = myInParts.Bound(aSolid, TopTools_ListOfShape());
 
       TopTools_ListIteratorOfListOfShape aItLS(aLInFaces);
       for (; aItLS.More(); aItLS.Next())
@@ -360,10 +353,7 @@ typedef BOPTools_Cnt<BOPAlgo_BuilderSolidFunctor,
 //function : BuildSplitSolids
 //purpose  : 
 //=======================================================================
-void BOPAlgo_Builder::BuildSplitSolids
-  (TopTools_DataMapOfShapeListOfShape& theInParts,
-   TopTools_DataMapOfShapeShape& theDraftSolids,
-   const Handle(NCollection_BaseAllocator)&  )
+void BOPAlgo_Builder::BuildSplitSolids(TopTools_DataMapOfShapeShape& theDraftSolids)
 {
   Standard_Boolean bFlagSD;
   Standard_Integer i, aNbS;
@@ -417,7 +407,7 @@ void BOPAlgo_Builder::BuildSplitSolids
       continue;
 
     const TopoDS_Shape& aSD = theDraftSolids.Find(aS);
-    const TopTools_ListOfShape* pLFIN = theInParts.Seek(aS);
+    const TopTools_ListOfShape* pLFIN = myInParts.Seek(aS);
     if (!pLFIN || pLFIN->IsEmpty())
     {
       aSolidsIm(aSolidsIm.Add(aS, TopTools_ListOfShape())).Append(aSD);
@@ -464,7 +454,35 @@ void BOPAlgo_Builder::BuildSplitSolids
   {
     BOPAlgo_SplitSolid& aBS = aVBS(k);
     aSolidsIm.Add(aBS.Solid(), aBS.Areas());
-    myReport->Merge(aBS.GetReport());
+
+    // Merge BuilderSolid's report into main report,
+    // assigning the solid with the warnings/errors which
+    // have been generated for it.
+    // Convert all errors of BuilderSolid into warnings for main report.
+    const Handle(Message_Report)& aBSReport = aBS.GetReport();
+    Message_Gravity anAlertTypes[2] = { Message_Warning, Message_Fail };
+    for (Standard_Integer iGravity = 0; iGravity < 2; iGravity++)
+    {
+      const Message_ListOfAlert& anLAlerts = aBSReport->GetAlerts(anAlertTypes[iGravity]);
+      for (Message_ListOfAlert::Iterator itA(anLAlerts); itA.More(); itA.Next())
+      {
+        Handle(Message_Alert) anAlert = itA.Value();
+
+        Handle(TopoDS_AlertWithShape) anAlertWithShape = Handle(TopoDS_AlertWithShape)::DownCast(itA.Value());
+        if (!anAlertWithShape.IsNull())
+        {
+          TopoDS_Shape aWarnShape;
+          BRep_Builder().MakeCompound(TopoDS::Compound(aWarnShape));
+          BRep_Builder().Add(aWarnShape, aBS.Solid());
+          BRep_Builder().Add(aWarnShape, anAlertWithShape->GetShape());
+
+          anAlertWithShape->SetShape(aWarnShape);
+          AddWarning(anAlertWithShape);
+        }
+        else
+          AddWarning(anAlert);
+      }
+    }
   }
   //
   // Add new solids to images map
index 21c2048..0e7b0b2 100644 (file)
@@ -332,97 +332,40 @@ void BOPAlgo_MakerVolume::FillInternalShapes(const TopTools_ListOfShape& theLSR)
   if (myAvoidInternalShapes) {
     return;
   }
-  //
+
   UserBreak();
-  //
-  Standard_Integer aNbSI;
-  TopAbs_ShapeEnum aType;
-  TopAbs_State aState; 
-  TopoDS_Iterator aItS;
-  BRep_Builder aBB;
+
+  // Get all non-compound shapes
+  TopTools_ListOfShape aLSC;
+  // Fence map
   TopTools_MapOfShape aMFence;
-  TopTools_IndexedMapOfShape aMSS;
-  TopTools_ListOfShape aLVE, aLSC, aLSIn;
-  TopTools_ListIteratorOfListOfShape aIt, aIt1;
-  //
-  // 1. Collect shapes to process: vertices, edges, wires
-  const TopTools_ListOfShape& anArguments = myDS->Arguments();
-  aIt.Initialize(anArguments);
-  for (; aIt.More(); aIt.Next()) {
-    const TopoDS_Shape& aS = aIt.Value();
-    BOPAlgo_Tools::TreatCompound(aS, aMFence, aLSC);
-  }
-  //
-  aIt.Initialize(aLSC);
-  for (; aIt.More(); aIt.Next()) {
-    const TopoDS_Shape& aS = aIt.Value();
-    aType = aS.ShapeType();
-    if (aType == TopAbs_WIRE) {
-      aItS.Initialize(aS);
-      for(; aItS.More(); aItS.Next()) {
-        const TopoDS_Shape& aE = aItS.Value();
-        if (aMFence.Add(aE)) {
-          aLVE.Append(aE);
-        }
+
+  TopTools_ListOfShape::Iterator itLA(myDS->Arguments());
+  for (; itLA.More(); itLA.Next())
+    BOPAlgo_Tools::TreatCompound(itLA.Value(), aMFence, aLSC);
+
+  // Get only edges and vertices from arguments
+  TopTools_ListOfShape aLVE;
+
+  itLA.Initialize(aLSC);
+  for (; itLA.More(); itLA.Next())
+  {
+    const TopoDS_Shape& aS = itLA.Value();
+    TopAbs_ShapeEnum aType = aS.ShapeType();
+    if (aType == TopAbs_WIRE)
+    {
+      for (TopoDS_Iterator it(aS); it.More(); it.Next())
+      {
+        const TopoDS_Shape& aSS = it.Value();
+        if (aMFence.Add(aSS))
+          aLVE.Append(aSS);
       }
     }
-    else if (aType == TopAbs_VERTEX || aType == TopAbs_EDGE) {
+    else if (aType == TopAbs_VERTEX || aType == TopAbs_EDGE)
       aLVE.Append(aS);
-    } 
-  }
-  //
-  aIt.Initialize(theLSR);
-  for (; aIt.More(); aIt.Next()) {
-    const TopoDS_Shape& aS = aIt.Value();
-    TopExp::MapShapes(aS, TopAbs_EDGE, aMSS);
-    TopExp::MapShapes(aS, TopAbs_VERTEX, aMSS);
-  }
-  //
-  aIt.Initialize(aLVE);
-  for (; aIt.More(); aIt.Next()) {
-    const TopoDS_Shape& aS = aIt.Value();
-    if (myImages.IsBound(aS)) {
-      const TopTools_ListOfShape &aLSp = myImages.Find(aS);
-      aIt1.Initialize(aLSp);
-      for (; aIt1.More(); aIt1.Next()) {
-        const TopoDS_Shape& aSp = aIt1.Value();
-        if (aMSS.Add(aSp)) {
-          aLSIn.Append(aSp);
-        }
-      }
-    }
-    else {
-      if (aMSS.Add(aS)) {
-        aLSIn.Append(aS);
-      }
-    }
-  }
-  //
-  aNbSI = aLSIn.Extent();
-  if (!aNbSI) {
-    return;
-  }
-  //
-  // 2. Settle internal vertices and edges into solids
-  aIt.Initialize(theLSR);
-  for (; aIt.More(); aIt.Next()) {
-    TopoDS_Solid aSd = *(TopoDS_Solid*)&aIt.Value();
-    //
-    aIt1.Initialize(aLSIn);
-    for (; aIt1.More(); ) {
-      TopoDS_Shape aSI = aIt1.Value();
-      aSI.Orientation(TopAbs_INTERNAL);
-      //
-      aState = BOPTools_AlgoTools::ComputeStateByOnePoint(aSI, aSd, 1.e-11, myContext);
-      if (aState == TopAbs_IN) {
-        aBB.Add(aSd, aSI);
-        aLSIn.Remove(aIt1);
-      }
-      else {
-        aIt1.Next();
-      }
-    }
   }
+
+  BOPAlgo_Tools::FillInternals(theLSR, aLVE, myImages, myContext);
 }
 
 //=======================================================================
index ae49545..fb2b15b 100644 (file)
@@ -1643,3 +1643,148 @@ void BOPAlgo_Tools::ClassifyFaces(const TopTools_ListOfShape& theFaces,
     theInParts.Add(aS, aLFIn);
   }
 }
+
+//=======================================================================
+//function : FillInternals
+//purpose  :
+//=======================================================================
+void BOPAlgo_Tools::FillInternals(const TopTools_ListOfShape& theSolids,
+                                  const TopTools_ListOfShape& theParts,
+                                  const TopTools_DataMapOfShapeListOfShape& theImages,
+                                  const Handle(IntTools_Context)& theContext)
+{
+  if (theSolids.IsEmpty() || theParts.IsEmpty())
+    return;
+
+  // Map the solids to avoid classification of the own shapes of the solids
+  TopTools_IndexedMapOfShape aMSSolids;
+  TopTools_ListOfShape::Iterator itLS(theSolids);
+  for (; itLS.More(); itLS.Next())
+  {
+    const TopoDS_Shape& aSolid = itLS.Value();
+    if (aSolid.ShapeType() == TopAbs_SOLID)
+    {
+      TopExp::MapShapes(aSolid, TopAbs_VERTEX, aMSSolids);
+      TopExp::MapShapes(aSolid, TopAbs_EDGE,   aMSSolids);
+      TopExp::MapShapes(aSolid, TopAbs_FACE,   aMSSolids);
+    }
+  }
+
+  // Extract BRep elements from the given parts and
+  // check them for possible splits
+  TopTools_ListOfShape aLPartsInput = theParts, aLParts;
+  TopTools_ListOfShape::Iterator itLP(aLPartsInput);
+  for (; itLP.More(); itLP.Next())
+  {
+    const TopoDS_Shape& aPart = itLP.Value();
+    switch (aPart.ShapeType())
+    {
+      case TopAbs_VERTEX:
+      case TopAbs_EDGE:
+      case TopAbs_FACE:
+      {
+        const TopTools_ListOfShape* pIm = theImages.Seek(aPart);
+        if (pIm)
+        {
+          TopTools_ListOfShape::Iterator itIm(*pIm);
+          for (; itIm.More(); itIm.Next())
+          {
+            const TopoDS_Shape& aPartIm = itIm.Value();
+            if (!aMSSolids.Contains(aPartIm))
+              aLParts.Append(aPartIm);
+          }
+        }
+        else if (!aMSSolids.Contains(aPart))
+          aLParts.Append(aPart);
+
+        break;
+      }
+      default:
+      {
+        for (TopoDS_Iterator it(aPart); it.More(); it.Next())
+          aLPartsInput.Append(it.Value());
+        break;
+      }
+    }
+  }
+
+  // Classify the given parts relatively solids.
+  // Add edges and vertices classified as IN into solids instantly,
+  // and collect faces classified as IN into a list for further shell creation
+
+  TopTools_DataMapOfShapeListOfShape anINFaces;
+  itLS.Initialize(theSolids);
+  for (; itLS.More(); itLS.Next())
+  {
+    const TopoDS_Shape& aSolid = itLS.Value();
+    if (aSolid.ShapeType() != TopAbs_SOLID)
+      continue;
+
+    TopoDS_Solid aSd = *(TopoDS_Solid*)&aSolid;
+
+    itLP.Initialize(aLParts);
+    for (; itLP.More();)
+    {
+      TopoDS_Shape aPart = itLP.Value();
+      TopAbs_State aState =
+        BOPTools_AlgoTools::ComputeStateByOnePoint(aPart, aSd, Precision::Confusion(), theContext);
+      if (aState == TopAbs_IN)
+      {
+        if (aPart.ShapeType() == TopAbs_FACE)
+        {
+          TopTools_ListOfShape *pFaces = anINFaces.ChangeSeek(aSd);
+          if (!pFaces)
+            pFaces = anINFaces.Bound(aSd, TopTools_ListOfShape());
+          pFaces->Append(aPart);
+        }
+        else
+        {
+          aPart.Orientation(TopAbs_INTERNAL);
+          BRep_Builder().Add(aSd, aPart);
+        }
+        aLParts.Remove(itLP);
+      }
+      else
+        itLP.Next();
+    }
+  }
+
+  // Make shells from faces and put them into solids
+  TopTools_DataMapOfShapeListOfShape::Iterator itM(anINFaces);
+  for (; itM.More(); itM.Next())
+  {
+    TopoDS_Solid aSd = *(TopoDS_Solid*)&itM.Key();
+    const TopTools_ListOfShape& aFaces = itM.Value();
+
+    TopoDS_Compound aCF;
+    BRep_Builder().MakeCompound(aCF);
+
+    TopTools_ListOfShape::Iterator itLF(aFaces);
+    for (; itLF.More(); itLF.Next())
+      BRep_Builder().Add(aCF, itLF.Value());
+
+    // Build blocks from the faces
+    TopTools_ListOfShape aLCB;
+    BOPTools_AlgoTools::MakeConnexityBlocks(aCF, TopAbs_EDGE, TopAbs_FACE, aLCB);
+
+    // Build shell from each block
+    TopTools_ListOfShape::Iterator itCB(aLCB);
+    for (; itCB.More(); itCB.Next())
+    {
+      const TopoDS_Shape& aCB = itCB.Value();
+
+      TopoDS_Shell aShell;
+      BRep_Builder().MakeShell(aShell);
+      // Add faces of the block to the shell
+      TopExp_Explorer expF(aCB, TopAbs_FACE);
+      for (; expF.More(); expF.Next())
+      {
+        TopoDS_Face aFInt = TopoDS::Face(expF.Current());
+        aFInt.Orientation(TopAbs_INTERNAL);
+        BRep_Builder().Add(aShell, aFInt);
+      }
+
+      BRep_Builder().Add(aSd, aShell);
+    }
+  }
+}
index 7d75bfa..50492e9 100644 (file)
@@ -194,6 +194,18 @@ public:
                                             const TopTools_DataMapOfShapeBox& theShapeBoxMap = TopTools_DataMapOfShapeBox(),
                                             const TopTools_DataMapOfShapeListOfShape& theSolidsIF = TopTools_DataMapOfShapeListOfShape());
 
+  //! Classifies the given parts relatively the given solids and
+  //! fills the solids with the parts classified as INTERNAL.
+  //!
+  //! @param theSolids  - The solids to put internals to
+  //! @param theParts   - The parts to classify relatively solids
+  //! @param theImages  - Possible images of the parts that has to be classified
+  //! @param theContext - Cashed geometrical tools to speed-up classifications
+  Standard_EXPORT static void FillInternals(const TopTools_ListOfShape& theSolids,
+                                            const TopTools_ListOfShape& theParts,
+                                            const TopTools_DataMapOfShapeListOfShape& theImages,
+                                            const Handle(IntTools_Context)& theContext);
+
 };
 
 #endif // _BOPAlgo_Tools_HeaderFile
index d4803b6..790495e 100644 (file)
@@ -90,6 +90,7 @@ void BOPTest::ReportAlerts(const Handle(Message_Report)& theReport)
 {
   // first report warnings, then errors
   Message_Gravity anAlertTypes[2] = { Message_Warning, Message_Fail };
+  TCollection_ExtendedString aMsgType[2] = { "Warning: ", "Error: " };
   for (int iGravity = 0; iGravity < 2; iGravity++)
   {
     // report shapes for the same type of alert together
@@ -104,7 +105,7 @@ void BOPTest::ReportAlerts(const Handle(Message_Report)& theReport)
 
       // get alert message
       Message_Msg aMsg (aIt.Value()->GetMessageKey());
-      TCollection_ExtendedString aText = aMsg.Get();
+      TCollection_ExtendedString aText = aMsgType[iGravity] + aMsg.Get();
 
       // collect all shapes if any attached to this alert
       if (BOPTest_Objects::DrawWarnShapes())
index 23bd525..ebbb605 100644 (file)
@@ -19,6 +19,7 @@
 #include <BOPAlgo_PaveFiller.hxx>
 #include <BOPAlgo_Section.hxx>
 #include <BOPAlgo_Splitter.hxx>
+#include <BOPDS_DS.hxx>
 #include <BOPTest.hxx>
 #include <BOPTest_DrawableShape.hxx>
 #include <BOPTest_Objects.hxx>
@@ -29,6 +30,7 @@
 #include <DrawTrSurf.hxx>
 #include <OSD_Timer.hxx>
 #include <TopoDS_Shape.hxx>
+#include <TopoDS.hxx>
 
 #include <stdio.h>
 #include <string.h>
@@ -38,6 +40,7 @@ static Standard_Integer bfillds  (Draw_Interpretor&, Standard_Integer, const cha
 static Standard_Integer bbuild   (Draw_Interpretor&, Standard_Integer, const char**);
 static Standard_Integer bbop     (Draw_Interpretor&, Standard_Integer, const char**);
 static Standard_Integer bsplit   (Draw_Interpretor&, Standard_Integer, const char**);
+static Standard_Integer buildbop (Draw_Interpretor&, Standard_Integer, const char**);
 
 //=======================================================================
 //function : PartitionCommands
@@ -55,6 +58,20 @@ void BOPTest::PartitionCommands(Draw_Interpretor& theCommands)
   theCommands.Add("bbuild" , "use bbuild r [-t]" , __FILE__, bbuild, g);
   theCommands.Add("bbop"   , "use bbop r op [-t]", __FILE__, bbop, g);
   theCommands.Add("bsplit" , "use bsplit r [-t]" , __FILE__, bsplit, g);
+
+  theCommands.Add("buildbop", "Builds the result of BOP basing on the GF.\n"
+                  " The command uses classification approach for building the result of BOP\n"
+                  " (thus it operates on solids only and can be used on open solids):\n"
+                  "  - FUSE is built from the faces OUT of all arguments\n"
+                  "  - COMMON is built from the faces IN any of the object/tools\n"
+                  "  - CUT is built from the objects faces OUT of the tools and tools faces IN the objects.\n"
+                  " Please note that history for solids will not be available.\n\n"
+                  " Usage: buildbop result -o s1 [s2 ...] -t s3 [s4 ...] -op operation (common/fuse/cut/tuc)\n"
+                  " Where:\n"
+                  " result      - result shape of the operation\n"
+                  " s1 s2 s3 s4 - arguments (solids) of the GF operation\n"
+                  " operation   - type of boolean operation",
+                  __FILE__, buildbop, g);
 }
 //=======================================================================
 //function : bfillds
@@ -419,3 +436,157 @@ Standard_Integer bsplit(Draw_Interpretor& di,
   DBRep::Set(a[1], aR);
   return 0;
 }
+
+//=======================================================================
+//function : buildbop
+//purpose  : 
+//=======================================================================
+Standard_Integer buildbop(Draw_Interpretor& di,
+                          Standard_Integer n,
+                          const char** a)
+{
+  if (n < 3)
+  {
+    di.PrintHelp(a[0]);
+    return 1;
+  }
+
+  BOPDS_PDS pDS = BOPTest_Objects::PDS();
+  if (!pDS)
+  {
+    di << "Error: perform intersection of arguments first";
+    return 1;
+  }
+
+  BOPAlgo_Builder *pBuilder = &BOPTest_Objects::Builder();
+  if (pBuilder->HasErrors())
+  {
+    di << "Error: there were problems during GF";
+    return 0;
+  }
+
+  if (pBuilder->Arguments().IsEmpty() ||
+      pBuilder->Shape().IsNull())
+  {
+    di << "Error: it seems the GF has not been yet performed";
+    return 1;
+  }
+
+  // Get arguments and operation
+  TopTools_ListOfShape aLObjects, aLTools;
+  BOPAlgo_Operation anOp = BOPAlgo_UNKNOWN;
+
+  for (Standard_Integer i = 2; i < n; ++i)
+  {
+    if (!strcmp(a[i], "-o") || !strcmp(a[i], "-t"))
+    {
+      if (i == (n - 1))
+      {
+        di << "Error: shapes are expected after the key " << a[i];
+        return 1;
+      }
+
+      TopTools_ListOfShape& aList = !strcmp(a[i], "-o") ? aLObjects : aLTools;
+      Standard_Integer j = i + 1;
+      for (; j < n; ++j)
+      {
+        if (a[j][0] == '-')
+        {
+          // reached the following key
+          i = j - 1;
+          break;
+        }
+        else
+        {
+          // Get the shape
+          TopoDS_Shape aS = DBRep::Get(a[j]);
+          if (aS.IsNull())
+          {
+            di << "Error: " << a[j] << " is a null shape";
+            return 1;
+          }
+          if (aS.ShapeType() != TopAbs_SOLID)
+          {
+            di << "Error: " << a[j] << " is not a solid";
+            return 1;
+          }
+          if (pDS->Index(aS) < 0)
+          {
+            di << "Error: " << a[j] << " is not an argument of GF";
+            return 1;
+          }
+          aList.Append(aS);
+        }
+      }
+      // End of arguments is reached
+      if (j == n) break;
+    }
+    else if (!strcmp(a[i], "-op"))
+    {
+      if (i == (n - 1))
+      {
+        di << "Error: operation type is expected after the key " << a[i];
+        return 1;
+      }
+
+      ++i;
+      if (!strcasecmp(a[i], "common"))
+        anOp = BOPAlgo_COMMON;
+      else if (!strcasecmp(a[i], "fuse"))
+        anOp = BOPAlgo_FUSE;
+      else if (!strcasecmp(a[i], "cut"))
+        anOp = BOPAlgo_CUT;
+      else if (!strcasecmp(a[i], "tuc"))
+        anOp = BOPAlgo_CUT21;
+      else
+      {
+        di << "Error: unknown operation type";
+        return 1;
+      }
+    }
+    else
+    {
+      di << "Error: " << a[i] << " invalid key";
+      return 1;
+    }
+  }
+
+  if (anOp == BOPAlgo_UNKNOWN)
+  {
+    di << "Error: operation has not been specified";
+    return 1;
+  }
+
+  Standard_Boolean hasObjects = !aLObjects.IsEmpty();
+  Standard_Boolean hasTools   = !aLTools.IsEmpty();
+  if (!hasObjects && !hasTools)
+  {
+    di << "Error: no shapes are given";
+    return 1;
+  }
+
+  // Create new report for the operation
+  Handle(Message_Report) aReport = new Message_Report;
+
+  // Build specific operation
+  pBuilder->BuildBOP(aLObjects, aLTools, anOp, aReport);
+
+  // Report alerts of the operation
+  BOPTest::ReportAlerts(aReport);
+
+  if (!aReport->GetAlerts(Message_Fail).IsEmpty())
+  {
+    return 0;
+  }
+
+  // Set history of Split operation into the session
+  if (BRepTest_Objects::IsHistoryNeeded())
+    BRepTest_Objects::SetHistory(pDS->Arguments(), *pBuilder);
+
+  // Result shape
+  const TopoDS_Shape& aR = pBuilder->Shape();
+  // Draw result shape
+  DBRep::Set(a[1], aR);
+
+  return 0;
+}
index ce6f0c6..b65d4aa 100644 (file)
@@ -593,18 +593,31 @@ TopAbs_State BOPTools_AlgoTools::ComputeStateByOnePoint
    const Standard_Real theTol,
    const Handle(IntTools_Context)& theContext)
 {
-  TopAbs_State aState;
-  TopAbs_ShapeEnum aType;
-  //
-  aState=TopAbs_UNKNOWN;
-  aType=theS.ShapeType();
-  if (aType==TopAbs_VERTEX) {
-    const TopoDS_Vertex& aV=(*(TopoDS_Vertex*)(&theS));
-    aState=BOPTools_AlgoTools::ComputeState(aV, theRef, theTol, theContext);
-  }
-  else if (aType==TopAbs_EDGE) {
-    const TopoDS_Edge& aE=(*(TopoDS_Edge*)(&theS));
-    aState=BOPTools_AlgoTools::ComputeState(aE, theRef, theTol, theContext);
+  TopAbs_State aState = TopAbs_UNKNOWN;
+  TopAbs_ShapeEnum aType = theS.ShapeType();
+
+  switch (aType)
+  {
+    case TopAbs_VERTEX:
+      aState = ComputeState(TopoDS::Vertex(theS), theRef, theTol, theContext);
+      break;
+    case TopAbs_EDGE:
+      aState = ComputeState(TopoDS::Edge(theS), theRef, theTol, theContext);
+      break;
+    case TopAbs_FACE:
+    {
+      TopTools_IndexedMapOfShape aBounds;
+      TopExp::MapShapes(theRef, TopAbs_EDGE, aBounds);
+      aState = ComputeState(TopoDS::Face(theS), theRef, theTol, aBounds, theContext);
+      break;
+    }
+    default:
+    {
+      TopoDS_Iterator it(theS);
+      if (it.More())
+        ComputeStateByOnePoint(it.Value(), theRef, theTol, theContext);
+      break;
+    }
   }
   return aState;
 }
index f234831..4a6635e 100644 (file)
 //function : FillIn3DParts
 //purpose  : 
 //=======================================================================
-  void BRepFeat_Builder::FillIn3DParts(TopTools_DataMapOfShapeListOfShape& theInParts,
-                                       TopTools_DataMapOfShapeShape& theDraftSolids,
-                                       const Handle(NCollection_BaseAllocator)& theAllocator)
+  void BRepFeat_Builder::FillIn3DParts(TopTools_DataMapOfShapeShape& theDraftSolids)
 {
   GetReport()->Clear();
-  //
-  Standard_Boolean bIsIN, bHasImage;
-  Standard_Integer aNbS, i, j, aNbFP, aNbFPx, aNbFIN, aNbLIF, aNbEFP;
-  TopAbs_ShapeEnum aType;  
-  TopAbs_State aState;
-  TopoDS_Iterator aIt, aItF;
-  BRep_Builder aBB;
-  TopoDS_Solid aSolidSp; 
-  TopoDS_Face aFP;
-  TopTools_ListIteratorOfListOfShape aItS, aItFP, aItEx;       
-  TopTools_MapIteratorOfMapOfShape aItMS, aItMS1;
-  //
-  TopTools_ListOfShape aLIF(theAllocator);
-  TopTools_MapOfShape aMFDone(100, theAllocator);
-  TopTools_MapOfShape aMSolids(100, theAllocator);
-  TopTools_MapOfShape aMFaces(100, theAllocator);
-  TopTools_MapOfShape aMFIN(100, theAllocator);
-  TopTools_IndexedMapOfShape aMS(100, theAllocator);
-  TopTools_IndexedDataMapOfShapeListOfShape aMEF(100, theAllocator);
-  //
-  theDraftSolids.Clear();
-  //
-  aNbS=myDS->NbSourceShapes();
-  for (i=0; i<aNbS; ++i) {
-    const BOPDS_ShapeInfo& aSI=myDS->ShapeInfo(i);
-    const TopoDS_Shape& aS=aSI.Shape();
-    //
-    aType=aSI.ShapeType();
-    switch(aType) {
-      case TopAbs_SOLID: {
-        aMSolids.Add(aS);
-        break;
-      }
-      //
-      case TopAbs_FACE: {
-        // all faces (originals or images)
-        if (myImages.IsBound(aS)) {
-          const TopTools_ListOfShape& aLS=myImages.Find(aS);
-          aItS.Initialize(aLS);
-          for (; aItS.More(); aItS.Next()) {
-            const TopoDS_Shape& aFx=aItS.Value();
-            if (!myRemoved.Contains(aFx)) {
-              aMFaces.Add(aFx);
-            }
-          }
-        }
-        else {
-          if (!myRemoved.Contains(aS)) {
-            aMFaces.Add(aS);
-          }
-        }
-        break;
-      }
-      //
-      default:
-        break;
+
+  BOPAlgo_Builder::FillIn3DParts(theDraftSolids);
+
+  // Clear the IN parts of the solids from the removed faces
+  TopTools_DataMapOfShapeListOfShape::Iterator itM(myInParts);
+  for (; itM.More(); itM.Next())
+  {
+    TopTools_ListOfShape& aList = itM.ChangeValue();
+    TopTools_ListOfShape::Iterator itL(aList);
+    for (; itL.More();)
+    {
+      if (myRemoved.Contains(itL.Value()))
+        aList.Remove(itL);
+      else
+        itL.Next();
     }
   }
-  //
-  aItMS.Initialize(aMSolids);
-  for (; aItMS.More(); aItMS.Next()) {
-    const TopoDS_Solid& aSolid=(*(TopoDS_Solid*)(&aItMS.Value()));
-    //
-    aMFDone.Clear();
-    aMFIN.Clear();
-    aMEF.Clear();
-    //
-    aBB.MakeSolid(aSolidSp);
-    // 
-    // Draft solid and its pure internal faces => aSolidSp, aLIF
-    aLIF.Clear();
-    BuildDraftSolid(aSolid, aSolidSp, aLIF);
-    aNbLIF=aLIF.Extent();
-    //
-    // 1 all faces/edges from aSolid [ aMS ]
-    bHasImage=Standard_False;
-    aMS.Clear();
-    aIt.Initialize(aSolid);
-    for (; aIt.More(); aIt.Next()) {
-      const TopoDS_Shape& aShell=aIt.Value();
-      //
-      if (myImages.IsBound(aShell)) {
-        bHasImage=Standard_True;
-        //
-        const TopTools_ListOfShape& aLS=myImages.Find(aShell);
-        aItS.Initialize(aLS);
-        for (; aItS.More(); aItS.Next()) {
-          const TopoDS_Shape& aSx=aItS.Value();
-          aMS.Add(aSx);
-          TopExp::MapShapes(aSx, TopAbs_FACE, aMS);
-          TopExp::MapShapes(aSx, TopAbs_EDGE, aMS);
-          TopExp::MapShapesAndAncestors(aSx, TopAbs_EDGE, TopAbs_FACE, aMEF);
-        }
-      }
-      else {
-        //aMS.Add(aShell);
-        TopExp::MapShapes(aShell, TopAbs_FACE, aMS);
-        TopExp::MapShapesAndAncestors(aShell, TopAbs_EDGE, TopAbs_FACE, aMEF);
-      }
-    }
-    //
-    // 2 all faces that are not from aSolid [ aLFP1 ]
-    TopTools_IndexedDataMapOfShapeListOfShape aMEFP(100, theAllocator);
-    TopTools_ListOfShape aLFP1(theAllocator);
-    TopTools_ListOfShape aLFP(theAllocator);
-    TopTools_ListOfShape aLCBF(theAllocator);
-    TopTools_ListOfShape aLFIN(theAllocator);
-    TopTools_ListOfShape aLEx(theAllocator);
-    //
-    // for all non-solid faces build EF map [ aMEFP ]
-    aItMS1.Initialize(aMFaces);
-    for (; aItMS1.More(); aItMS1.Next()) {
-      const TopoDS_Shape& aFace=aItMS1.Value();
-      if (!aMS.Contains(aFace)) {
-        TopExp::MapShapesAndAncestors(aFace, TopAbs_EDGE, TopAbs_FACE, aMEFP);
-      }
-    }
-    //
-    // among all faces from aMEFP select these that have same edges
-    // with the solid (i.e aMEF). These faces will be treated first 
-    // to prevent the usage of 3D classifier.
-    // The full list of faces to process is aLFP1. 
-    aNbEFP=aMEFP.Extent();
-    for (j=1; j<=aNbEFP; ++j) {
-      const TopoDS_Shape& aE=aMEFP.FindKey(j);
-      //
-      if (aMEF.Contains(aE)) { // !!
-        const TopTools_ListOfShape& aLF=aMEFP(j);
-        aItFP.Initialize(aLF);
-        for (; aItFP.More(); aItFP.Next()) {
-          const TopoDS_Shape& aF=aItFP.Value();
-          if (aMFDone.Add(aF)) {
-            aLFP1.Append(aF);
-          }
-        }
-      }
-      else {
-        aLEx.Append(aE);
-      }
-    }
-    //
-    aItEx.Initialize(aLEx);
-    for (; aItEx.More(); aItEx.Next()) {
-      const TopoDS_Shape& aE=aItEx.Value();
-      const TopTools_ListOfShape& aLF=aMEFP.FindFromKey(aE);
-      aItFP.Initialize(aLF);
-      for (; aItFP.More(); aItFP.Next()) {
-        const TopoDS_Shape& aF=aItFP.Value();
-        if (aMFDone.Add(aF)) {
-          //aLFP2.Append(aF);
-          aLFP1.Append(aF);
-        }
-      }
-    }
-    //
-    //==========
-    //
-    // 3 Process faces aLFP1
-    aMFDone.Clear();
-    aNbFP=aLFP1.Extent();
-    aItFP.Initialize(aLFP1);
-    for (; aItFP.More(); aItFP.Next()) {
-      const TopoDS_Shape& aSP=aItFP.Value();
-      if (!aMFDone.Add(aSP)) {
-        continue;
-      }
-      
-      //
-      // first face to process
-      aFP=(*(TopoDS_Face*)(&aSP));
-      bIsIN=BOPTools_AlgoTools::IsInternalFace(aFP, aSolidSp, aMEF, 1.e-14, myContext);
-      aState=(bIsIN) ? TopAbs_IN : TopAbs_OUT;
-      //
-      // collect faces to process [ aFP is the first ]
-      aLFP.Clear();
-      aLFP.Append(aFP);
-      aItS.Initialize(aLFP1);
-      for (; aItS.More(); aItS.Next()) {
-        const TopoDS_Shape& aSk=aItS.Value();
-        if (!aMFDone.Contains(aSk)) {
-          aLFP.Append(aSk);
-        }
-      }
-      //
-      // Connexity Block that spreads from aFP the Bound 
-      // or till the end of the block itself
-      aLCBF.Clear();
-      BOPTools_AlgoTools::MakeConnexityBlock(aLFP, aMS, aLCBF, theAllocator);
-      //
-      // fill states for the Connexity Block 
-      aItS.Initialize(aLCBF);
-      for (; aItS.More(); aItS.Next()) {
-        const TopoDS_Shape& aSx=aItS.Value();
-        aMFDone.Add(aSx);
-        if (aState==TopAbs_IN) {
-          aMFIN.Add(aSx);
-        }
-      }
-      //
-      aNbFPx=aMFDone.Extent();
-      if (aNbFPx==aNbFP) {
-        break;
-      }
-    }//for (; aItFP.More(); aItFP.Next())
-    //
-    // faces Inside aSolid
-    aLFIN.Clear();
-    aNbFIN=aMFIN.Extent();
-    if (aNbFIN || aNbLIF) {
-      aItMS1.Initialize(aMFIN);
-      for (; aItMS1.More(); aItMS1.Next()) {
-        const TopoDS_Shape& aFIn=aItMS1.Value();
-        aLFIN.Append(aFIn);
-      }
-      //
-      aItS.Initialize(aLIF);
-      for (; aItS.More(); aItS.Next()) {
-        const TopoDS_Shape& aFIN=aItS.Value();
-        aLFIN.Append(aFIN);
-      }
-      //
-      theInParts.Bind(aSolid, aLFIN);
-    }
-    if (aNbFIN || bHasImage) {
-      theDraftSolids.Bind(aSolid, aSolidSp);
-    }
-  }// for (; aItMS.More(); aItMS.Next()) {
 }
index 383bff4..62a5241 100644 (file)
@@ -117,7 +117,14 @@ protected:
   Standard_EXPORT virtual void Prepare() Standard_OVERRIDE;
   
   //! Function is redefined to avoid the usage of removed faces.
-  Standard_EXPORT virtual void FillIn3DParts (TopTools_DataMapOfShapeListOfShape& theInParts, TopTools_DataMapOfShapeShape& theDraftSolids, const Handle(NCollection_BaseAllocator)& theAllocator) Standard_OVERRIDE;
+  Standard_EXPORT virtual void FillIn3DParts (TopTools_DataMapOfShapeShape& theDraftSolids) Standard_OVERRIDE;
+
+  //! Avoid the check for open solids and always use the splits
+  //! of solids for building the result shape.
+  virtual Standard_Boolean CheckArgsForOpenSolid() Standard_OVERRIDE
+  {
+    return Standard_False;
+  }
 
 
   TopTools_MapOfShape myShapes;
index 602e5f9..798f6de 100644 (file)
@@ -28,7 +28,10 @@ public:
 
   //! Returns contained shape
   const TopoDS_Shape& GetShape() const { return myShape; }
-  
+
+  //! Sets the shape
+  void SetShape(const TopoDS_Shape& theShape) { myShape = theShape; }
+
   //! Returns false.
   virtual Standard_EXPORT Standard_Boolean SupportsMerge () const Standard_OVERRIDE;
 
index ed6703a..a7f4092 100644 (file)
@@ -1,9 +1,8 @@
-puts "TODO #22911 ALL: Error : The area of result shape is"
-puts "TODO OCC25735 ALL: Faulty shapes in variables faulty_1 to"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 restore [locate_data_file CTO908_topo101-o1.brep] obj1
 restore [locate_data_file CTO908_topo101-t1.brep] tool1
 
 bcut result obj1 tool1
-checkprops result -s 0
+checkprops result -s 18467.3
 checkview -display result -2d -s -otherwise { obj1 tool1 } -path ${imagedir}/${test_image}.png
index 12be623..a5f8443 100644 (file)
@@ -1,8 +1,7 @@
-puts "TODO #22911 ALL: Error : The area of result shape is"
-puts "TODO OCC25735 ALL: Faulty shapes in variables faulty_1 to"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 restore [locate_data_file CTO908_topo101-o2.brep] obj2
 restore [locate_data_file CTO908_topo101-t2.brep] tool2
 bcut result obj2 tool2
-checkprops result -s 0
+checkprops result -s 15696.8
 checkview -display result -2d -s -otherwise { obj2 tool2 } -path ${imagedir}/${test_image}.png
index ea110c2..5ca4b7f 100644 (file)
@@ -1,9 +1,8 @@
-puts "TODO #22911 ALL: Error : The area of result shape is"
-puts "TODO OCC25735 ALL: Faulty shapes in variables faulty_1 to"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 restore [locate_data_file CTO908_topo102-o.brep] obj
 restore [locate_data_file CTO908_topo102-t.brep] tool
 
 bcut result obj tool
-checkprops result -s 0
+checkprops result -s 44135.8
 checkview -display result -2d -s -otherwise { obj tool } -path ${imagedir}/${test_image}.png
index a30a165..6decb91 100644 (file)
@@ -1,9 +1,8 @@
-puts "TODO #22911 ALL: Error : The area of result shape is"
-puts "TODO OCC25735 ALL: Faulty shapes in variables faulty_1 to"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 restore [locate_data_file CTO908_topo103-o.brep] obj
 restore [locate_data_file CTO908_topo103-t.brep] tool
 
 bcut result obj tool
-checkprops result -s 0
+checkprops result -s 133676
 checkview -display result -2d -s -otherwise { obj tool } -path ${imagedir}/${test_image}.png
index 606350d..332c5ae 100644 (file)
@@ -1,9 +1,9 @@
-puts "TODO OCC25735 ALL: Faulty shapes in variables faulty_1 to"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 restore [locate_data_file CTO908_topo104-o1.brep] wheel
 restore [locate_data_file CTO908_topo104-o2.brep] jante
 
 bfuse result wheel jante
 
-checkprops result -s 40059.9
+checkprops result -s 28962.2
 checkview -display result -2d -otherwise { wheel jante } -s -path ${imagedir}/${test_image}.png
\ No newline at end of file
index c114c1d..4a295c5 100644 (file)
@@ -29,4 +29,5 @@
 029 splitter
 030 history
 031 removefeatures
-032 simplify
\ No newline at end of file
+032 simplify
+033 opensolid
\ No newline at end of file
diff --git a/tests/boolean/opensolid/A1 b/tests/boolean/opensolid/A1
new file mode 100644 (file)
index 0000000..95dda57
--- /dev/null
@@ -0,0 +1,41 @@
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
+
+box b1 10 10 10
+box b2 5 0 0 10 10 10
+
+shape s1 So
+shape s2 So
+shape sh1 Sh
+shape sh2 Sh
+
+foreach f [lrange [explode b1 f] 0 4] { add $f sh1 }
+foreach f [lrange [explode b2 f] 0 4] { add $f sh2 }
+
+add sh1 s1
+add sh2 s2
+
+bclearobjects
+bcleartools
+baddobjects s1
+baddtools s2
+bfillds
+
+bbop r0 0
+bbop r1 1
+bbop r2 2
+bbop r3 3
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checknbshapes r1 -vertex 16 -edge 26 -wire 11 -face 11 -shell 1 -solid 1
+checkprops r1 -s 650
+
+foreach r {r0 r2 r3} {
+  checknbshapes $r -vertex 8 -edge 12 -wire 5 -face 5 -shell 1 -solid 1
+  checkprops $r -s 350
+}
diff --git a/tests/boolean/opensolid/A2 b/tests/boolean/opensolid/A2
new file mode 100644 (file)
index 0000000..18d86f0
--- /dev/null
@@ -0,0 +1,45 @@
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
+
+box b1 10 10 10
+box b2 2.5 0 0 5 10 5
+
+shape s1 So
+shape s2 So
+shape sh1 Sh
+shape sh2 Sh
+
+foreach f [lrange [explode b1 f] 0 4] { add $f sh1 }
+foreach f [lrange [explode b2 f] 0 4] { add $f sh2 }
+
+add sh1 s1
+add sh2 s2
+
+bclearobjects
+bcleartools
+baddobjects s1
+baddtools s2
+bfillds
+
+bbop r0 0
+bbop r1 1
+bbop r2 2
+bbop r3 3
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checknbshapes r0 -vertex 8 -edge 12 -wire 5 -face 5 -shell 1 -solid 1
+checkprops r0 -s 200
+
+checknbshapes r1 -vertex 16 -edge 24 -wire 9 -face 9 -shell 1 -solid 1
+checkprops r1 -s 500
+
+checknbshapes r2 -vertex 16 -edge 24 -wire 8 -face 8 -shell 1 -solid 1
+checkprops r2 -s 500
+
+checknbshapes r3 -vertex 0 -edge 0 -wire 0 -face 0 -shell 0 -solid 0
+checkprops r3 -s empty
diff --git a/tests/boolean/opensolid/A3 b/tests/boolean/opensolid/A3
new file mode 100644 (file)
index 0000000..db57478
--- /dev/null
@@ -0,0 +1,44 @@
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
+
+box b1 -15 0 0 20 5 5
+box b2 5 5 20
+
+shape s1 So
+shape s2 So
+shape sh1 Sh
+shape sh2 Sh
+
+foreach f [lrange [explode b1 f] 1 5] { add $f sh1 }
+foreach f [lrange [explode b2 f] 0 4] { add $f sh2 }
+
+add sh1 s1
+add sh2 s2
+
+bclearobjects
+bcleartools
+baddobjects s1
+baddtools s2
+bfillds
+
+bbop r0 0
+bbop r1 1
+bbop r2 2
+bbop r3 3
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checknbshapes r0 -vertex 8 -edge 12 -wire 6 -face 6 -shell 1 -solid 1
+checkprops r0 -s 150
+
+checknbshapes r1 -vertex 16 -edge 28 -wire 12 -face 12 -shell 1 -solid 1
+checkprops r1 -s 700
+
+foreach r {r2 r3} {
+  checknbshapes $r -vertex 8 -edge 12 -wire 5 -face 5 -shell 1 -solid 1
+  checkprops $r -s 325
+}
diff --git a/tests/boolean/opensolid/A4 b/tests/boolean/opensolid/A4
new file mode 100644 (file)
index 0000000..66721ef
--- /dev/null
@@ -0,0 +1,60 @@
+box b1 40 10 10
+box b2 0 0 30 40 10 10
+box b3 10 10 40
+box b4 30 0 0 10 10 40
+
+bclearobjects
+bcleartools
+baddobjects b1 b2 b3 b4
+bfillds
+bbuild r
+
+# fuse of all
+buildbop r1 -o b1 b2 b3 b4 -op fuse
+buildbop r2 -o b1 b2 b3 -t b4 -op fuse
+buildbop r3 -o b1 b2 -t b3 b4 -op fuse
+buildbop r4 -o b1 -t b2 b3 b4 -op fuse
+buildbop r5 -t b1 b2 b3 b4 -op fuse
+
+foreach r {r1 r2 r3 r4 r5} {
+  checkshape $r
+  checknbshapes $r -vertex 32 -edge 64 -wire 32 -face 32 -shell 1 -solid 1
+  checkprops $r -s 4800 -v 12000
+}
+
+# Cut
+buildbop r1 -o b1 b2 -t b3 b4 -op cut
+buildbop r2 -o b1 b2 -t b3 b4 -op tuc
+
+foreach r {r1 r2} {
+  checkshape $r
+  checknbshapes $r -vertex 16 -edge 24 -wire 12 -face 12 -shell 2 -solid 2
+  checkprops $r -s 2000 -v 4000
+}
+
+buildbop r1 -o b1 -t b3 b4 -op cut
+buildbop r2 -o b2 -t b3 b4 -op cut
+
+foreach r {r1 r2} {
+  checkshape $r
+  checknbshapes $r -vertex 8 -edge 12 -wire 6 -face 6 -shell 1 -solid 1
+  checkprops $r -s 1000 -v 2000
+}
+
+# Common
+buildbop r1 -o b1 b2 -t b3 b4 -op common
+checkshape r1
+checknbshapes r1 -vertex 32 -edge 48 -wire 24 -face 24 -shell 4 -solid 4
+checkprops r1 -s 2400 -v 4000
+
+buildbop r1 -o b1 -t b3 b4 -op common
+buildbop r2 -o b2 -t b3 b4 -op common
+
+checkshape r1
+checkshape r2
+
+foreach r {r1 r2} {
+  checkshape $r
+  checknbshapes $r -vertex 16 -edge 24 -wire 12 -face 12 -shell 2 -solid 2
+  checkprops $r -s 1200 -v 2000
+}
diff --git a/tests/boolean/opensolid/A5 b/tests/boolean/opensolid/A5
new file mode 100644 (file)
index 0000000..721baf5
--- /dev/null
@@ -0,0 +1,32 @@
+box b1 10 2 2
+box b2 2 0.5 -3 1 1 8
+box b3 7 0.5 -3 1 1 8
+invert b3
+
+bclearobjects
+bcleartools
+baddobjects b1
+baddtools b2 b3
+bfillds
+bbuild r
+
+buildbop r0 -o b1 -t b2 b3 -op common
+buildbop r1 -o b1 -t b2 b3 -op fuse
+buildbop r2 -o b1 -t b2 b3 -op cut
+buildbop r3 -o b1 -t b2 b3 -op tuc
+
+checkshape r0
+checkprops r0 -s 110 -v 38
+checknbshapes r0 -vertex 24 -edge 36 -wire 20 -face 16 -shell 2 -solid 2
+
+checkshape r1
+checkprops r1 -s 28 -v -6
+checknbshapes r1 -vertex 16 -edge 24 -wire 12 -face 12 -shell 2 -solid 2
+
+checkshape r2
+checkprops r2 -s 10 -v 2
+checknbshapes r2 -vertex 8 -edge 12 -wire 6 -face 6 -shell 1 -solid 1
+
+checkshape r3
+checkprops r3 -s 164 -v -46
+checknbshapes r3 -vertex 40 -edge 60 -wire 32 -face 28 -shell 3 -solid 3
diff --git a/tests/boolean/opensolid/A6 b/tests/boolean/opensolid/A6
new file mode 100644 (file)
index 0000000..9bd8d89
--- /dev/null
@@ -0,0 +1,34 @@
+box b1 10 2 2
+box b2 5 0 0 5 5 5
+box b3 6 0 0 1 1 1
+
+bclearobjects
+bcleartools
+baddobjects b1
+baddtools b2 b3
+bfillds
+bbuild r
+
+buildbop r0 -o b1 -t b2 b3 -op common
+buildbop r1 -o b1 -t b2 b3 -op fuse
+buildbop r2 -o b1 -t b2 b3 -op cut
+buildbop r3 -o b1 -t b2 b3 -op tuc
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checkprops r0 -s 56 -v 20
+checknbshapes r0 -vertex 16 -edge 25 -wire 12 -face 12 -shell 2 -solid 2
+
+checkprops r1 -s 190 -v 145
+checknbshapes r1 -vertex 24 -edge 38 -wire 16 -face 16 -shell 1 -solid 1
+
+checkprops r2 -s 48 -v 20
+checknbshapes r2 -vertex 8 -edge 12 -wire 6 -face 6 -shell 1 -solid 1
+
+checkprops r3 -s 142 -v 105
+checknbshapes r3 -vertex 12 -edge 18 -wire 8 -face 8 -shell 1 -solid 1
diff --git a/tests/boolean/opensolid/A7 b/tests/boolean/opensolid/A7
new file mode 100644 (file)
index 0000000..860026d
--- /dev/null
@@ -0,0 +1,59 @@
+box b1 10 10 10
+box b2 1 1 1 8 8 8
+box b3 -2 3 3 14 4 4
+
+bclearobjects
+bcleartools
+baddobjects b1 b2 b3
+bfillds
+bbuild r
+
+buildbop r0 -o b3 -t b1 b2 -op common
+buildbop r1 -o b3 -t b1 b2 -op fuse
+buildbop r2 -o b3 -t b1 b2 -op cut
+buildbop r3 -o b3 -t b1 b2 -op tuc
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checkprops r0 -s 256 -v 160
+checknbshapes r0 -vertex 16 -edge 28 -wire 16 -face 16 -shell 3 -solid 3
+
+checkprops r1 -s 664 -v 1064
+checknbshapes r1 -vertex 24 -edge 36 -wire 18 -face 16 -shell 1 -solid 1
+
+checkprops r2 -s 128 -v 64
+checknbshapes r2 -vertex 16 -edge 24 -wire 12 -face 12 -shell 2 -solid 2
+
+checkprops r3 -s 1432 -v 840
+checknbshapes r3 -vertex 32 -edge 52 -wire 28 -face 24 -shell 2 -solid 2
+
+
+
+buildbop r0 -o b2 -t b1 b3 -op common
+buildbop r1 -o b2 -t b1 b3 -op fuse
+buildbop r2 -o b2 -t b1 b3 -op cut
+buildbop r3 -o b2 -t b1 b3 -op tuc
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checkprops r0 -s 640 -v 512
+checknbshapes r0 -vertex 16 -edge 24 -wire 14 -face 12 -shell 2 -solid 2
+
+checkprops r1 -s 664 -v 1064
+checknbshapes r1 -vertex 24 -edge 36 -wire 18 -face 16 -shell 1 -solid 1
+
+checkprops r2 -s empty -v empty
+checknbshapes r2 -vertex 0 -edge 0 -wire 0 -face 0 -shell 0 -solid 0
+
+checkprops r3 -s 1176 -v 552
+checknbshapes r3 -vertex 40 -edge 64 -wire 38 -face 34 -shell 5 -solid 5
diff --git a/tests/boolean/opensolid/A8 b/tests/boolean/opensolid/A8
new file mode 100644 (file)
index 0000000..6d9a1cc
--- /dev/null
@@ -0,0 +1,33 @@
+box b1 10 10 10
+box b2 -2 -2 2 7 14 6
+box b3 5 -2 2 7 14 6
+bclearobjects
+bcleartools
+baddobjects b1
+baddtools b2 b3
+bfillds
+bbuild r
+
+buildbop r0 -o b1 -t b3 b2 -op common
+buildbop r1 -o b1 -t b3 b2 -op fuse
+buildbop r2 -o b1 -t b3 b2 -op cut
+buildbop r3 -o b1 -t b3 b2 -op tuc
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checkprops r0 -s 560 -v 600
+checknbshapes r0 -vertex 12 -edge 20 -wire 11 -face 11 -shell 2 -solid 2
+
+checkprops r1 -s 888 -v 1576
+checknbshapes r1 -vertex 32 -edge 50 -wire 20 -face 20 -shell 1 -solid 1
+
+checkprops r2 -s 560 -v 400
+checknbshapes r2 -vertex 20 -edge 30 -wire 14 -face 14 -shell 2 -solid 2
+
+checkprops r3 -s 816 -v 576
+checknbshapes r3 -vertex 24 -edge 40 -wire 18 -face 18 -shell 2 -solid 2
diff --git a/tests/boolean/opensolid/A9 b/tests/boolean/opensolid/A9
new file mode 100644 (file)
index 0000000..e58196b
--- /dev/null
@@ -0,0 +1,56 @@
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
+
+box b1 10 10 10
+box b2 5 0 0 10 10 10
+
+shape s1 So
+shape s2 So
+shape sh1 Sh
+shape sh2 Sh
+
+foreach f [lrange [explode b1 f] 0 4] { add $f sh1 }
+foreach f [lrange [explode b2 f] 0 4] { add $f sh2 }
+
+add sh1 s1
+add sh2 s2
+
+polyline p 1 1 1 1 9 1 1 9 9 1 1 9 1 1 1
+mkplane f p
+orientation f I
+shape shell_int1 Sh
+add f shell_int1
+add shell_int1 s1
+
+copy shell_int1 shell_int2
+ttranslate shell_int2 13 0 0
+add shell_int2 s2
+
+bclearobjects
+bcleartools
+baddobjects s1
+baddtools s2
+bfillds
+
+bbop r0 0
+bbop r1 1
+bbop r2 2
+bbop r3 3
+
+foreach r {r0 r1 r2 r3} {
+  checkshape $r
+  if {![regexp "OK" [bopcheck $r]]} {
+    puts "Error: shape is self-interfered"
+  }
+}
+
+checknbshapes r0 -vertex 8 -edge 12 -wire 5 -face 5 -shell 1 -solid 1
+checkprops r0 -s 350
+
+checknbshapes r1 -vertex 16 -edge 26 -wire 11 -face 11 -shell 1 -solid 1
+checkprops r1 -s 650
+
+# results of CUT operations should contain an extra shell
+foreach r {r2 r3} {
+  checknbshapes $r -vertex 12 -edge 16 -wire 6 -face 6 -shell 2 -solid 1
+  checkprops $r -s 414
+}
\ No newline at end of file
index 90c64a1..0a03d53 100755 (executable)
@@ -1,5 +1,4 @@
-puts "TODO OCC12345 ALL: Error : The area of result shape is"
-puts "TODO OCC12345 ALL: Faulty shapes in variables faulty_1 to faulty_"
+puts "REQUIRED All: Faulty shapes in variables faulty_1 to faulty_2"
 
 puts "========================"
 puts " OCC420 "
@@ -21,6 +20,6 @@ renamevar a_1 a
 
 bcut result a b
 
-checkprops result -s 
+checkprops result -s 135.092
 checkshape result
 checkview -display result -2d -path ${imagedir}/${test_image}.png