0029683: Add functionality to make the TopoDS_Shape periodic in 3D space
authoremv <emv@opencascade.com>
Mon, 19 Mar 2018 04:50:06 +0000 (07:50 +0300)
committerapn <apn@opencascade.com>
Fri, 7 Dec 2018 15:49:44 +0000 (18:49 +0300)
Implementation of the new class *BOPAlgo_MakePeriodic* for making the shape periodic in 3D space.
Periodicity of the shape means that the shape can be repeated in any periodic direction any number of times without creation of the new geometry or splits.
The idea of this algorithm is to make the shape look similarly on the opposite sides or on the period bounds of periodic directions.
It does not mean that the opposite sides of the shape will be mirrored. It just means the the opposite sides of the shape should be split by each other and obtain the same geometry on opposite sides.
Such approach will allow repeating the shape, i.e. translating the copy of a shape on the period, without creation of new geometry because there will be no coinciding parts of different dimension.

Draw commands for the new algorithm:
* makeperiodic - makes the shape periodic in required directions;
* repeatshape - repeats the periodic shape in requested periodic direction;
* periodictwins - returns the periodic twins for the shape;
* clearrepetitions - clears all previous repetitions of the periodic shape.

Documentation & test cases for the algorithm.

31 files changed:
dox/user_guides/draw_test_harness/draw_test_harness.md
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im001.png [new file with mode: 0644]
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im002.png [new file with mode: 0644]
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im003.png [new file with mode: 0644]
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im004.png [new file with mode: 0644]
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im005.png [new file with mode: 0644]
dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png [new file with mode: 0644]
dox/user_guides/modeling_algos/modeling_algos.md
src/BOPAlgo/BOPAlgo.msg
src/BOPAlgo/BOPAlgo_Alerts.hxx
src/BOPAlgo/BOPAlgo_BOPAlgo_msg.pxx
src/BOPAlgo/BOPAlgo_Builder_1.cxx
src/BOPAlgo/BOPAlgo_MakePeriodic.cxx [new file with mode: 0644]
src/BOPAlgo/BOPAlgo_MakePeriodic.hxx [new file with mode: 0644]
src/BOPAlgo/BOPAlgo_PaveFiller.cxx
src/BOPAlgo/BOPAlgo_PaveFiller.hxx
src/BOPAlgo/BOPAlgo_Splitter.cxx
src/BOPAlgo/BOPAlgo_Splitter.hxx
src/BOPAlgo/FILES
src/BOPTest/BOPTest.cxx
src/BOPTest/BOPTest.hxx
src/BOPTest/BOPTest_PeriodicityCommands.cxx [new file with mode: 0644]
src/BOPTest/FILES
tests/boolean/grids.list
tests/boolean/periodicity/A1 [new file with mode: 0644]
tests/boolean/periodicity/A2 [new file with mode: 0644]
tests/boolean/periodicity/A3 [new file with mode: 0644]
tests/boolean/periodicity/A4 [new file with mode: 0644]
tests/boolean/periodicity/A5 [new file with mode: 0644]
tests/boolean/periodicity/A6 [new file with mode: 0644]
tests/bugs/modalg_7/bug29502

index 241da61..fdabe65 100644 (file)
@@ -5860,6 +5860,7 @@ The following topics are covered in the eight sections of this chapter:
   * Topological operations, or booleans.
   * Drafting and blending.
   * Defeaturing.
+  * Making shapes periodic in 3D space.
   * Analysis of shapes.
 
 
@@ -7191,6 +7192,66 @@ parallel - enables the parallel processing mode.
 ~~~~
 
 
+@subsection occt_draw_makeperiodic 3D Model Periodicity
+
+Draw module for @ref occt_modalg_makeperiodic "making the shape periodic" includes the following commands:
+* **makeperiodic** - makes the shape periodic in required directions;
+* **repeatshape** - repeats the periodic shape in requested periodic direction;
+* **periodictwins** - returns the periodic twins for the shape;
+* **clearrepetitions** - clears all previous repetitions of the periodic shape.
+
+@subsubsection occt_draw_makeperiodic_makeperiodic makeperiodic
+
+The command makes the shape periodic in the required directions with the required period.
+If trimming is given it trims the shape to fit the requested period.
+
+Syntax:
+~~~~
+makeperiodic result shape [-x/y/z period [-trim first]]
+
+Where:
+result        - resulting periodic shape;
+shape         - input shape to make it periodic:
+-x/y/z period - option to make the shape periodic in X, Y or Z direction with the given period;
+-trim first   - option to trim the shape to fit the required period, starting the period in first.
+~~~~
+
+@subsubsection occt_draw_makeperiodic_repeatshape repeatshape
+
+The command repeats the periodic shape in periodic direction requested number of time.
+The result contains the all the repeated shapes glued together.
+The command should be called after **makeperiodic** command.
+
+Syntax:
+~~~~
+repeatshape result -x/y/z times
+
+Where:
+result       - resulting shape;
+-x/y/z times - direction for repetition and number of repetitions (negative number of times means the repetition in negative direction).
+~~~~
+
+@subsubsection occt_draw_makeperiodic_periodictwins periodictwins
+
+For the given shape the command returns the identical shapes located on the opposite sides of the periodic direction.
+All periodic twins should have the same geometry.
+The command should be called after **makeperiodic** command.
+
+Syntax:
+~~~~
+periodictwins twins shape
+
+Where:
+twins - periodic twins for the given shape
+shape - shape to find the twins for
+~~~~
+
+@subsubsection occt_draw_makeperiodic_clearrepetitions clearrepetitions
+
+The command clears all previous repetitions of the periodic shape allowing to start the repetitions over.
+No arguments are needed for the command.
+
+
 @subsection occt_draw_7_9  Analysis of topology and geometry
 
 Analysis of shapes includes commands to compute length, area, volumes and inertial properties, as well as to compute some aspects impacting shape validity.
@@ -8551,7 +8612,7 @@ Where:
 
 @subsubsection occt_draw_bop_options_warn Drawing warning shapes
 
-**bdrawwarnshapes** command enables/disables drawing of waring shapes of BOP algorithms.
+**bdrawwarnshapes** command enables/disables drawing of warning shapes of BOP algorithms.
 
 Syntax:
 ~~~~
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im001.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im001.png
new file mode 100644 (file)
index 0000000..b7fea40
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im001.png differ
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im002.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im002.png
new file mode 100644 (file)
index 0000000..23fa211
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im002.png differ
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im003.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im003.png
new file mode 100644 (file)
index 0000000..96c6bba
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im003.png differ
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im004.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im004.png
new file mode 100644 (file)
index 0000000..e014051
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im004.png differ
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im005.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im005.png
new file mode 100644 (file)
index 0000000..b68d0c3
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im005.png differ
diff --git a/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png
new file mode 100644 (file)
index 0000000..74ebe85
Binary files /dev/null and b/dox/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png differ
index d01a6ca..b5bf481 100644 (file)
@@ -3438,3 +3438,168 @@ Here are the few examples of defeaturing of the model containing boxes with blen
   <td>@figure{/user_guides/modeling_algos/images/modeling_algos_rf_im029.png,"Result",220}</td></td>
 </tr>
 </table>
+
+
+@section occt_modalg_makeperiodic 3D Model Periodicity
+
+Open CASCADE Technology provides tools for making an arbitrary 3D model (or just shape) periodic in 3D space in the specified directions.
+
+Periodicity of the shape means that the shape can be repeated in any periodic direction any number of times without creation of the new geometry or splits.
+The idea of this algorithm is to make the shape look similarly on the opposite sides or on the period bounds of periodic directions.
+It does not mean that the opposite sides of the shape will be mirrored. It just means that the opposite sides of the shape should be split by each other and obtain the same geometry on opposite sides.
+Such approach will allow repeating the shape, i.e. translating the copy of a shape on the period, without creation of new geometry because there will be no coinciding parts of different dimension.
+
+For better understanding of what periodicity means lets create a simple prism and make it periodic.
+The following draw script creates the L-shape prism with extensions 10x5x10:
+~~~~
+polyline p 0 0 0 0 0 10 5 0 10 5 0 5 10 0 5 10 0 0 0 0 0
+mkplane f p
+prism s f 0 5 0
+~~~~
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im001.png,"Shape to make periodic",220}
+
+Making this shape periodic in X, Y and Z directions with the periods matching the shape's extensions should make the splits of negative X and Z sides of the shape. The shape is already similar on opposite sides of Y directions, thus no new splits is expected.
+Here is the shape after making it periodic:
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im002.png,"Periodic shape",220}
+And here is the repetition of the shape once in X and Z directions:
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im003.png,"Repeated shape",220}
+
+The OCCT Shape Periodicity tools also allows making the shape periodic with the period not matching the shape's extensions. Let's make the shape periodic with 11, 6 and 11 for X, Y, Z periods accordingly.
+Such values of periods mean that there will be a gap between repeated shapes, and since during repetition the opposite sides do not touch the shape will not be split at all.
+Here is the repetition of the shape once in X and Z directions:
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im004.png,"Repeated shape",220}
+As expected, the shape is not split and the repeated elements do not touch.
+
+If necessary the algorithm will trim the shape to fit into the requested period by splitting it with the planes limiting the shape's requested periods.
+E.g. let's make the L-shape periodic only in X direction with the period 2 starting at X parameter 4:
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im005.png,"Periodic trimmed shape",220}
+
+@subsection occt_modalg_makeperiodic_how_it_works How the shape is made periodic
+
+For making the shape periodic in certain direction the algorithm performs the following steps:
+* Creates the copy of the shape and moves it on the period into negative side of the requested direction;
+* Splits the negative side of the shape by the moved copy, ensuring copying of the geometry from positive side to negative;
+* Creates the copy of the shape (with already split negative side) and moves it on the period into the positive side of the requested direction;
+* Splits the positive side of the shape by the moved copy, ensuring copying of the geometry from negative side to positive.
+
+Repeated copying of the geometry ensures that the corner edges of the periodic shape will have the same geometry on opposite sides of all periodic directions.
+
+Thus, in the periodic shape the geometry from positive side of the shape is always copied on the negative side of periodic directions.
+
+@subsection occt_modalg_makeperiodic_association Opposite shapes association
+
+The algorithm also associates the identical (or twin) shapes located on the opposite sides of the periodic shape. By the construction, the twin shapes should always have the same geometry and distanced from each other on the period.
+It is possible that the shape does not have any twins. It means that when repeating this shape will not touch the opposite side of the shape. In the example when the periods of the shape are grater than its extensions, non of the sub-shapes has a twin.
+
+@subsection occt_modalg_makeperiodic_repetition Periodic shape repetition
+
+The algorithm also provides the methods to repeat the periodic shape in periodic directions. To repeat shape the algorithm makes the requested number of copies of the shape and translates them one by one on the time * period value.
+After all copies are made and translated they are glued to have valid shape.
+The subsequent repetitions are performed on the repeated shape, thus e.g. repeating the shape two times in any periodic direction will create result containing three shapes (original plus two copies).
+Single subsequent repetition in any direction will result already in 6 shapes.
+
+The repetitions can be cleared and started over.
+
+@subsection occt_modalg_makeperiodic_history History support
+
+The algorithm supports the history of shapes modifications, thus it is possible to track how the shapes have been changed to make it periodic and what new shapes have been created during repetitions.
+Both split history and history of periodic shape repetition are available here. Note, that all repeated shapes are stored as generated into the history.
+
+
+*BRepTools_History* is used for history support.
+
+@subsection occt_modalg_makeperiodic_errors Errors/Warnings
+
+The algorithm supports the Error/Warning reporting system which allows obtaining the extended overview of the errors and warning occurred during the operation.
+As soon as any error appears the algorithm stops working. The warnings allow continuing the job, informing the user that something went wrong.
+The algorithm returns the following alerts:
+* *BOPAlgo_AlertNoPeriodicityRequired* - Error alert is given if no periodicity has been requested in any direction;
+* *BOPAlgo_AlertUnableToTrim* - Error alert is given if the trimming of the shape for fitting it into requested period has failed;
+* *BOPAlgo_AlertUnableToMakeIdentical* - Error alert is given if splitting of the shape by its moved copies has failed;
+* *BOPAlgo_AlertUnableToRepeat* - Warning alert is given if the gluing of the repeated shapes has failed.
+
+For more information on the error/warning reporting system please see the chapter @ref occt_algorithms_ers "Errors and warnings reporting system" of Boolean operations user guide.
+
+@subsection occt_modalg_makeperiodic_usage Usage
+
+The algorithm is implemented in the class *BOPAlgo_MakePeriodic*.
+Here is the example of its usage on the API level:
+~~~~
+TopoDS_Shape aShape = ...;                 // The shape to make periodic
+Standard_Boolean bMakeXPeriodic = ...;     // Flag for making or not the shape periodic in X direction
+Standard_Real aXPeriod = ...;              // X period for the shape
+Standard_Boolean isXTrimmed = ...;         // Flag defining whether it is necessary to trimming
+                                           // the shape to fit to X period
+Standard_Real aXFirst = ...;               // Start of the X period
+                                           // (really necessary only if the trimming is requested)
+Standard_Boolean bRunParallel = ...;       // Parallel processing mode or single
+
+BOPAlgo_MakePeriodic aPeriodicityMaker;                   // Periodicity maker
+aPeriodicityMaker.SetShape(aShape);                       // Set the shape
+aPeriodicityMaker.MakeXPeriodic(bMakePeriodic, aXPeriod); // Making the shape periodic in X direction
+aPeriodicityMaker.SetTrimmed(isXTrimmed, aXFirst);        // Trim the shape to fit X period
+aPeriodicityMaker.SetRunParallel(bRunParallel);           // Set the parallel processing mode
+aPeriodicityMaker.Perform();                              // Performing the operation
+
+if (aPeriodicityMaker.HasErrors())                        // Check for the errors
+{
+  // errors treatment
+  Standard_SStream aSStream;
+  aPeriodicityMaker.DumpErrors(aSStream);
+  return;
+}
+if (aPeriodicityMaker.HasWarnings())                      // Check for the warnings
+{
+  // warnings treatment
+  Standard_SStream aSStream;
+  aPeriodicityMaker.DumpWarnings(aSStream);
+}
+const TopoDS_Shape& aPeriodicShape = aPeriodicityMaker.Shape(); // Result periodic shape
+
+aPeriodicityMaker.XRepeat(2);                                    // Making repetitions
+const TopoDS_Shape& aRepeat = aPeriodicityMaker.RepeatedShape(); // Getting the repeated shape
+aPeriodicityMaker.ClearRepetitions();                            // Clearing the repetitions
+~~~~
+
+Please note, that the class is based on the options class *BOPAlgo_Options*, which provides the following options for the algorithm:
+* Error/Warning reporting system;
+* Parallel processing mode.
+The other options of the base class are not supported here and will have no effect.
+
+All the history information obtained during the operation is stored into *BRepTools_History* object and available through *History()* method:
+~~~~
+// Get the history object
+const Handle(BRepTools_History)& BOPAlgo_MakePeriodic::History();
+~~~~
+
+For the usage of the MakePeriodic algorithm on the Draw level the following commands have been implemented:
+* **makeperiodic**
+* **repeatshape**
+* **periodictwins**
+* **clearrepetitions**
+
+For more details on the periodicity commands please refer the @ref occt_draw_makeperiodic "Periodicity commands" of the Draw test harness user guide.
+
+To track the history of a shape modification during MakePeriodic operation the @ref occt_draw_hist "standard history commands" can be used.
+
+To have possibility to access the error/warning shapes of the operation use the *bdrawwarnshapes* command before running the algorithm (see command usage in the @ref occt_algorithms_ers "Errors and warnings reporting system" of Boolean operations user guide).
+
+@subsection occt_modalg_makeperiodic_examples Examples
+
+Imagine that you need to make the drills in the plate on the same distance from each other. To model this process it is necessary to make a lot of cylinders (simulating the drills) and cut these cylinders from the plate.
+With the periodicity tool, the process looks very simple:
+~~~~
+# create plate 100 x 100
+box plate -50 -50 0 100 100 1
+# create a single drill with radius 1
+pcylinder drill 1 1
+# locate the drill in the left corner
+ttranslate drill -48 -48 0
+# make the drill periodic with 4 as X and Y periods, so the distance between drills will be 2
+makeperiodic drill drill -x 4 -trim -50 -y 4 -trim -50
+# repeat the drill to fill the plate, in result we get net of 25 x 25 drills
+repeatshape drills -x 24 -y 24
+# cut the drills from the plate
+bcut result plate drills
+~~~~
+@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png,"Plate with drills",220}
index 4e5bd1c..abef3cc 100644 (file)
@@ -100,3 +100,15 @@ Unable to orient the shape correctly
 
 .BOPAlgo_AlertUnknownShape
 Shape is unknown for operation
+
+.BOPAlgo_AlertNoPeriodicityRequired
+No periodicity has been requested for the shape
+
+.BOPAlgo_AlertUnableToTrim
+Unable to trim the shape for making it periodic (BOP Common fails)
+
+.BOPAlgo_AlertUnableToMakeIdentical
+Unable to make the shape to look identical on opposite sides (Splitter fails)
+
+.BOPAlgo_AlertUnableToRepeat
+Unable to repeat the shape (Gluer fails)
index 8274528..21960d8 100644 (file)
@@ -30,7 +30,7 @@ DEFINE_SIMPLE_ALERT(BOPAlgo_AlertBuilderFailed)
 //! The intersection of the arguments has failed
 DEFINE_SIMPLE_ALERT(BOPAlgo_AlertIntersectionFailed)
 
-//! The type of Boolean Operation is not set
+//! More than one argument is provided
 DEFINE_SIMPLE_ALERT(BOPAlgo_AlertMultipleArguments)
 
 //! The Pave Filler (the intersection tool) has not been created
@@ -114,4 +114,16 @@ DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToOrientTheShape)
 //! Shape is unknown for operation
 DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnknownShape)
 
+//! No periodicity has been requested for the shape
+DEFINE_SIMPLE_ALERT(BOPAlgo_AlertNoPeriodicityRequired)
+
+//! Unable to trim the shape for making it periodic (BOP Common fails)
+DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToTrim)
+
+//! Unable to make the shape to look identical on opposite sides (Splitter fails)
+DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToMakeIdentical)
+
+//! Unable to repeat the shape (Gluer fails)
+DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToRepeat)
+
 #endif // _BOPAlgo_Alerts_HeaderFile
index 61ea072..1647b22 100644 (file)
@@ -102,4 +102,16 @@ static const char BOPAlgo_BOPAlgo_msg[] =
   "Unable to orient the shape correctly\n"
   "\n"
   ".BOPAlgo_AlertUnknownShape\n"
-  "Shape is unknown for operation\n";
+  "Shape is unknown for operation\n"
+  "\n"
+  ".BOPAlgo_AlertNoPeriodicityRequired\n"
+  "No periodicity has been requested for the shape\n"
+  "\n"
+  ".BOPAlgo_AlertUnableToTrim\n"
+  "Unable to trim the shape for making it periodic (BOP Common fails)\n"
+  "\n"
+  ".BOPAlgo_AlertUnableToMakeIdentical\n"
+  "Unable to make the shape to look identical on opposite sides (Splitter fails)\n"
+  "\n"
+  ".BOPAlgo_AlertUnableToRepeat\n"
+  "Unable to repeat the shape (Gluer fails)\n";
index 97ec6c7..90767d6 100644 (file)
@@ -112,32 +112,35 @@ void BOPAlgo_Builder::FillImagesVertices()
 //function : BuildResult
 //purpose  : 
 //=======================================================================
-  void BOPAlgo_Builder::BuildResult(const TopAbs_ShapeEnum theType)
+void BOPAlgo_Builder::BuildResult(const TopAbs_ShapeEnum theType)
 {
-  TopAbs_ShapeEnum aType;
-  BRep_Builder aBB;
-  TopTools_MapOfShape aM;
-  TopTools_ListIteratorOfListOfShape aIt, aItIm;
-  //
-  aIt.Initialize(myArguments);
-  for (; aIt.More(); aIt.Next()) {
-    const TopoDS_Shape& aS=aIt.Value();
-    aType=aS.ShapeType();
-    if (aType==theType) {
-      if (myImages.IsBound(aS)){
-        const TopTools_ListOfShape& aLSIm=myImages.Find(aS);
-        aItIm.Initialize(aLSIm);
-        for (; aItIm.More(); aItIm.Next()) {
-          const TopoDS_Shape& aSIm=aItIm.Value();
-          if (aM.Add(aSIm)) {
-            aBB.Add(myShape, aSIm);
-          }
-        }
-      }
-      else {
-        if (aM.Add(aS)) {
-          aBB.Add(myShape, aS);
-        }
+  // Fence map
+  TopTools_MapOfShape aMFence;
+  // Iterate on all arguments of given type
+  // and add their images into result
+  TopTools_ListIteratorOfListOfShape aItA(myArguments);
+  for (; aItA.More(); aItA.Next())
+  {
+    const TopoDS_Shape& aS = aItA.Value();
+    if (aS.ShapeType() != theType)
+      continue;
+    // Get images
+    const TopTools_ListOfShape* pLSIm = myImages.Seek(aS);
+    if (!pLSIm)
+    {
+      // No images -> add the argument shape itself into result
+      if (aMFence.Add(aS))
+        BRep_Builder().Add(myShape, aS);
+    }
+    else
+    {
+      // Add images of the argument shape into result
+      TopTools_ListIteratorOfListOfShape aItIm(*pLSIm);
+      for (; aItIm.More(); aItIm.Next())
+      {
+        const TopoDS_Shape& aSIm = aItIm.Value();
+        if (aMFence.Add(aSIm))
+          BRep_Builder().Add(myShape, aSIm);
       }
     }
   }
diff --git a/src/BOPAlgo/BOPAlgo_MakePeriodic.cxx b/src/BOPAlgo/BOPAlgo_MakePeriodic.cxx
new file mode 100644 (file)
index 0000000..1b32d93
--- /dev/null
@@ -0,0 +1,617 @@
+// Created on: 2018-03-16
+// Created by: Eugeny MALTCHIKOV
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <BOPAlgo_MakePeriodic.hxx>
+
+#include <BOPAlgo_Alerts.hxx>
+
+#include <Bnd_Box.hxx>
+
+#include <BOPAlgo_Builder.hxx>
+#include <BOPAlgo_PaveFiller.hxx>
+
+#include <BRepAlgoAPI_Common.hxx>
+#include <BRepAlgoAPI_Splitter.hxx>
+
+#include <BRepBndLib.hxx>
+
+#include <BRepBuilderAPI_Transform.hxx>
+
+#include <BRepPrimAPI_MakeBox.hxx>
+
+#include <gp_Pln.hxx>
+
+#include <Precision.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
+
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+
+// Periodic/Trim/Repeat directions
+static const gp_Dir MY_DIRECTIONS[3] = { gp::DX(),
+                                         gp::DY(),
+                                         gp::DZ() };
+
+//=======================================================================
+//function : Perform
+//purpose  : Performs the operation
+//=======================================================================
+void BOPAlgo_MakePeriodic::Perform()
+{
+  // Check the validity of input data
+  CheckData();
+  if (HasErrors())
+    return;
+
+  // Trim the shape to fit to the required period in
+  // required periodic directions
+  Trim();
+  if (HasErrors())
+    return;
+
+  // Make the shape identical on the opposite sides in
+  // required periodic directions
+  MakeIdentical();
+  if (HasErrors())
+    return;
+}
+
+//=======================================================================
+//function : CheckData
+//purpose  : Checks the validity of input data
+//=======================================================================
+void BOPAlgo_MakePeriodic::CheckData()
+{
+  if ( (!IsXPeriodic() || XPeriod() < Precision::Confusion())
+    && (!IsYPeriodic() || YPeriod() < Precision::Confusion())
+    && (!IsZPeriodic() || ZPeriod() < Precision::Confusion()))
+  {
+    // Add error informing the user that no periodicity is required
+    // or no valid period is set.
+
+    AddError(new BOPAlgo_AlertNoPeriodicityRequired());
+    return;
+  }
+}
+
+//=======================================================================
+//function : AddToShape
+//purpose  : Adds the shape <theWhat> to the shape <theWhere>
+//=======================================================================
+static void AddToShape(const TopoDS_Shape& theWhat,
+                       TopoDS_Shape& theWhere)
+{
+  if (theWhere.IsNull())
+    BRep_Builder().MakeCompound(TopoDS::Compound(theWhere));
+  BRep_Builder().Add(theWhere, theWhat);
+}
+//=======================================================================
+//function : AddToShape
+//purpose  : Adds the shape in the list <theLWhat> to the shape <theWhere>
+//=======================================================================
+static void AddToShape(const TopTools_ListOfShape& theLWhat,
+                       TopoDS_Shape& theWhere)
+{
+  TopTools_ListIteratorOfListOfShape it(theLWhat);
+  for (; it.More(); it.Next())
+    AddToShape(it.Value(), theWhere);
+}
+
+//=======================================================================
+//function : Trim
+//purpose  : Make the trim of the shape to fit to the periodic bounds.
+//=======================================================================
+void BOPAlgo_MakePeriodic::Trim()
+{
+  // Check if trim is required at all
+  if (IsInputXTrimmed() &&
+      IsInputYTrimmed() &&
+      IsInputZTrimmed())
+      return;
+
+  // Compute bounding box for the shape to use it as a starting
+  // volume for trimming. If required, the volume will be modified
+  // to the requested trimming size in requested directions.
+  Bnd_Box aBox;
+  BRepBndLib::Add(myInputShape, aBox);
+  // Enlarge box to avoid overlapping with the shape
+  aBox.Enlarge(0.1 * sqrt(aBox.SquareExtent()));
+
+  // Get Corner points of the bounding box
+  gp_Pnt aPMin = aBox.CornerMin();
+  gp_Pnt aPMax = aBox.CornerMax();
+
+  // Update corner points according to the requested trim parameters
+  for (Standard_Integer i = 0; i < 3; ++i)
+  {
+    if (IsInputTrimmed(i))
+      continue;
+
+    aPMin.SetCoord(i + 1, PeriodFirst(i));
+    aPMax.SetCoord(i + 1, PeriodFirst(i) + Period(i));
+  }
+
+  // Build Trimming solid using corner points
+  BRepPrimAPI_MakeBox aMBox(aPMin, aPMax);
+  const TopoDS_Shape& aTrimBox = aMBox.Solid();
+
+  // Perform trimming of the shape by solid
+  BRepAlgoAPI_Common aCommon;
+  // Set Object
+  TopTools_ListOfShape anObj;
+  anObj.Append(myInputShape);
+  aCommon.SetArguments(anObj);
+  // Set Tool
+  TopTools_ListOfShape aTool;
+  aTool.Append(aTrimBox);
+  aCommon.SetTools(aTool);
+  // Set the parallel processing mode
+  aCommon.SetRunParallel(myRunParallel);
+  // Build
+  aCommon.Build();
+  if (aCommon.HasErrors())
+  {
+    // Unable to trim the shape
+    // Merge errors from Common operation
+    myReport->Merge(aCommon.GetReport());
+    // Add new error saving the shapes for analysis
+    TopoDS_Compound aWS;
+    AddToShape(myInputShape, aWS);
+    AddToShape(aTrimBox, aWS);
+    AddError(new BOPAlgo_AlertUnableToTrim(aWS));
+    return;
+  }
+  // Get the trimmed shape
+  myShape = aCommon.Shape();
+  // Fill the History for the object only
+  mySplitHistory = new BRepTools_History();
+  mySplitHistory->Merge(anObj, aCommon);
+}
+
+//=======================================================================
+//function : MakeIdentical
+//purpose  : Make the shape look the same on the opposite sides in the
+//           required periodic directions.
+//=======================================================================
+void BOPAlgo_MakePeriodic::MakeIdentical()
+{
+  if (myShape.IsNull())
+    myShape = myInputShape;
+
+  if (mySplitHistory.IsNull())
+    mySplitHistory = new BRepTools_History;
+
+  // Split the negative side of the shape with the geometry
+  // located on the positive side
+  SplitNegative();
+  if (HasErrors())
+    return;
+
+  // Split the positive side of the shape with the geometry
+  // located on the negative side.
+  // Make sure that the opposite sides have identical geometries.
+  // Make associations between identical opposite shapes.
+  SplitPositive();
+
+  myHistory = new BRepTools_History();
+  myHistory->Merge(mySplitHistory);
+}
+
+//=======================================================================
+//function : SplitNegative
+//purpose  : Split the negative side of the shape with the geometry
+//           located on the positive side.
+//=======================================================================
+void BOPAlgo_MakePeriodic::SplitNegative()
+{
+  // Copy geometry from positive side of the shape to the negative first.
+  // So, translate the shape in negative periodic directions only.
+  //
+  // To avoid conflicts when copying geometries from positive periodic sides
+  // perform split of each periodic side in a separate operation.
+  for (Standard_Integer i = 0; i < 3; ++i)
+  {
+    if (!IsPeriodic(i))
+      continue;
+
+    // Translate the shape to the negative side
+    gp_Trsf aNegTrsf;
+    aNegTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i].Reversed());
+    BRepBuilderAPI_Transform aNegT(myShape, aNegTrsf, Standard_False);
+
+    // Split the negative side of the shape.
+    TopTools_ListOfShape aTools;
+    aTools.Append(aNegT.Shape());
+    SplitShape(aTools, mySplitHistory);
+  }
+}
+
+//=======================================================================
+//function : AddTwin
+//purpose  : Associates the shape <theS> with the shape <theTwin> in the map.
+//=======================================================================
+static void AddTwin(const TopoDS_Shape& theS,
+                    const TopoDS_Shape& theTwin,
+                    TopTools_DataMapOfShapeListOfShape& theMap)
+{
+  TopTools_ListOfShape *aTwins = theMap.ChangeSeek(theS);
+  if (!aTwins)
+  {
+    theMap.Bound(theS, TopTools_ListOfShape())->Append(theTwin);
+    return;
+  }
+
+  // Check if the twin shape is not yet present in the list
+  TopTools_ListIteratorOfListOfShape itLT(*aTwins);
+  for (; itLT.More(); itLT.Next())
+  {
+    if (theTwin.IsSame(itLT.Value()))
+      break;
+  }
+
+  if (!itLT.More())
+    aTwins->Append(theTwin);
+}
+
+//=======================================================================
+//function : SplitPositive
+//purpose  : Split the positive side of the shape with the geometry of the
+//           negative side. Associate the identical opposite sub-shapes.
+//=======================================================================
+void BOPAlgo_MakePeriodic::SplitPositive()
+{
+  // Prepare map of the sub-shapes of the input shape to make
+  // associations of the opposite shapes
+  TopTools_IndexedMapOfShape aSubShapesMap;
+  TopExp::MapShapes(myShape, aSubShapesMap);
+  const Standard_Integer aNbS = aSubShapesMap.Extent();
+
+  // Translate the shape to the positive periodic directions to make the
+  // shapes look identical on the opposite sides.
+  TopTools_ListOfShape aTools;
+
+  // Remember the history of shapes translation
+  TopTools_IndexedDataMapOfShapeListOfShape aTranslationHistMap;
+
+  // Make translations for all periodic directions
+  for (Standard_Integer i = 0; i < 3; ++i)
+  {
+    if (!IsPeriodic(i))
+      continue;
+
+    // Translate the shape to the positive side
+    gp_Trsf aPosTrsf;
+    aPosTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i]);
+    BRepBuilderAPI_Transform aTranslator(myShape, aPosTrsf, Standard_False);
+    aTools.Append(aTranslator.Shape());
+
+    // Fill the translation history map
+    for (Standard_Integer j = 1; j <= aNbS; ++j)
+    {
+      const TopoDS_Shape& aS = aSubShapesMap(j);
+      if (BRepTools_History::IsSupportedType(aS))
+      {
+        const TopTools_ListOfShape& aSM = aTranslator.Modified(aS);
+        TopTools_ListOfShape* pTS = aTranslationHistMap.ChangeSeek(aS);
+        if (!pTS)
+          pTS = &aTranslationHistMap(aTranslationHistMap.Add(aS, TopTools_ListOfShape()));
+        pTS->Append(aSM.First());
+      }
+    }
+  }
+
+  // Keep the split shape history and history of tools modifications
+  // during the split for making association of the opposite identical shapes
+  Handle(BRepTools_History) aSplitShapeHist = new BRepTools_History,
+                            aSplitToolsHist = new BRepTools_History;
+  // Split the positive side of the shape
+  SplitShape(aTools, aSplitShapeHist, aSplitToolsHist);
+  if (HasErrors())
+    return;
+
+  mySplitHistory->Merge(aSplitShapeHist);
+
+  // Make associations between identical opposite sub-shapes
+  const Standard_Integer aNbSH = aTranslationHistMap.Extent();
+  for (Standard_Integer i = 1; i <= aNbSH; ++i)
+  {
+    const TopoDS_Shape* pS = &aTranslationHistMap.FindKey(i);
+    const TopTools_ListOfShape& aSIm = aSplitShapeHist->Modified(*pS);
+    if (aSIm.Extent() == 1)
+      pS = &aSIm.First();
+    else if (aSIm.Extent() > 1)
+      continue;
+
+    const TopTools_ListOfShape& aLTranslated = aTranslationHistMap(i);
+
+    TopTools_ListIteratorOfListOfShape itLT(aLTranslated);
+    for (; itLT.More(); itLT.Next())
+    {
+      const TopoDS_Shape& aT = itLT.Value();
+      // Get shapes modifications during the split
+      const TopTools_ListOfShape& aTSplits = aSplitToolsHist->Modified(aT);
+
+      // Associate the shapes to each other
+      TopTools_ListIteratorOfListOfShape itSp(aTSplits);
+      for (; itSp.More(); itSp.Next())
+      {
+        const TopoDS_Shape& aSp = itSp.Value();
+        AddTwin(*pS, aSp, myTwins);
+        AddTwin(aSp, *pS, myTwins);
+      }
+    }
+  }
+}
+
+//=======================================================================
+//function : SplitShape
+//purpose  : Splits the shape by the given tools
+//=======================================================================
+void BOPAlgo_MakePeriodic::SplitShape(const TopTools_ListOfShape& theTools,
+                                      Handle(BRepTools_History) theSplitShapeHistory,
+                                      Handle(BRepTools_History) theSplitToolsHistory)
+{
+  // Make sure that the geometry from the tools will be copied to the split
+  // shape. For that, the tool shapes should be given to the Boolean Operations
+  // algorithm before the shape itself. This will make all coinciding parts
+  // use the geometry of the first argument.
+
+  // Intersection tool for passing ordered arguments
+  BOPAlgo_PaveFiller anIntersector;
+  anIntersector.SetArguments(theTools);
+  // Add the shape
+  anIntersector.AddArgument(myShape);
+  // Use gluing to speed-up intersections
+  anIntersector.SetGlue(BOPAlgo_GlueShift);
+  // Use safe input mode, to avoid reusing geometry of the shape
+  anIntersector.SetNonDestructive(Standard_True);
+  // Set parallel processing mode
+  anIntersector.SetRunParallel(myRunParallel);
+  // Perform Intersection of the arguments
+  anIntersector.Perform();
+  // Check for the errors
+  if (anIntersector.HasErrors())
+  {
+    // Unable to split the shape on opposite sides
+    // Copy the intersection errors
+    myReport->Merge(anIntersector.GetReport());
+    // Add new error saving the shapes for analysis
+    TopoDS_Compound aWS;
+    AddToShape(theTools, aWS);
+    AddToShape(myShape, aWS);
+    AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
+    return;
+  }
+
+  // Perform the splitting of the shape with the precomputed intersection results
+  BRepAlgoAPI_Splitter aSplitter(anIntersector);
+  // Set Object
+  TopTools_ListOfShape anObj;
+  anObj.Append(myShape);
+  aSplitter.SetArguments(anObj);
+  // Set Tools
+  aSplitter.SetTools(theTools);
+  // Use Gluing
+  aSplitter.SetGlue(BOPAlgo_GlueShift);
+  // Set parallel processing mode
+  aSplitter.SetRunParallel(myRunParallel);
+  // Perform splitting
+  aSplitter.Build();
+  // Check for the errors
+  if (aSplitter.HasErrors())
+  {
+    // Unable to split the shape on opposite sides
+    // Copy the splitter errors
+    myReport->Merge(aSplitter.GetReport());
+    // Add new error saving the shape for analysis
+    TopoDS_Compound aWS;
+    AddToShape(theTools, aWS);
+    AddToShape(myShape, aWS);
+    AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
+    return;
+  }
+  // Get the split shape
+  myShape = aSplitter.Shape();
+  // Remember the split history
+  if (!theSplitShapeHistory.IsNull())
+    theSplitShapeHistory->Merge(anObj, aSplitter);
+  if (!theSplitToolsHistory.IsNull())
+    theSplitToolsHistory->Merge(theTools, aSplitter);
+}
+
+//=======================================================================
+//function : RepeatShape
+//purpose  : Repeats the shape in the required periodic direction
+//=======================================================================
+const TopoDS_Shape& BOPAlgo_MakePeriodic::RepeatShape(const Standard_Integer theDir,
+                                                      const Standard_Integer theTimes)
+{
+  if (myRepeatedShape.IsNull())
+    myRepeatedShape = myShape;
+
+  if (!IsPeriodic(theDir))
+    return myRepeatedShape;
+
+  if (theTimes == 0)
+    return myRepeatedShape;
+
+  // Get the shape's period in the required direction
+  const Standard_Integer id = BOPAlgo_MakePeriodic::ToDirectionID(theDir);
+  if (myRepeatPeriod[id] < Precision::Confusion())
+    myRepeatPeriod[id] = Period(id);
+  const Standard_Real aPeriod = myRepeatPeriod[id];
+
+  // Coefficient to define in which direction the repetition will be performed:
+  // theTimes is positive - in positive direction;
+  // theTimes is negative - in negative direction.
+  const Standard_Integer iDir = theTimes > 0 ? 1 : -1;
+
+  // Create the translation history - all translated shapes will be
+  // created as Generated from the shape.
+  BRepTools_History aTranslationHistory;
+  TopTools_IndexedMapOfShape aSubShapesMap;
+  TopExp::MapShapes(myRepeatedShape, aSubShapesMap);
+  const Standard_Integer aNbS = aSubShapesMap.Extent();
+
+  // Add shapes for gluing
+  TopTools_ListOfShape aShapes;
+  // Add the shape itself
+  aShapes.Append(myRepeatedShape);
+  for (Standard_Integer i = 1; i <= aNbS; ++i)
+  {
+    const TopoDS_Shape& aS = aSubShapesMap(i);
+    if (BRepTools_History::IsSupportedType(aS))
+      aTranslationHistory.AddGenerated(aS, aS);
+  }
+
+  // Create translated copies of the shape
+  for (Standard_Integer i = 1; i <= Abs(theTimes); ++i)
+  {
+    gp_Trsf aTrsf;
+    aTrsf.SetTranslationPart(iDir * i * aPeriod * MY_DIRECTIONS[id]);
+    BRepBuilderAPI_Transform aTranslator(myRepeatedShape, aTrsf, Standard_False);
+    aShapes.Append(aTranslator.Shape());
+
+    // Fill the translation history
+    for (Standard_Integer j = 1; j <= aNbS; ++j)
+    {
+      const TopoDS_Shape& aS = aSubShapesMap(j);
+      if (BRepTools_History::IsSupportedType(aS))
+      {
+        const TopTools_ListOfShape& aLT = aTranslator.Modified(aS);
+        aTranslationHistory.AddGenerated(aS, aLT.First());
+      }
+    }
+  }
+
+  // Update the history with the translation History
+  myHistory->Merge(aTranslationHistory);
+
+  // Glue the translated shapes all together
+  BOPAlgo_Builder aGluer;
+  aGluer.SetArguments(aShapes);
+  // Avoid intersections of the sub-shapes
+  aGluer.SetGlue(BOPAlgo_GlueFull);
+  // Set parallel processing mode
+  aGluer.SetRunParallel(myRunParallel);
+  // Perform gluing
+  aGluer.Perform();
+  if (aGluer.HasErrors())
+  {
+    // Repetition in this direction is not possible
+    // Add warning saving the shapes for analysis
+    TopoDS_Compound aWS;
+    AddToShape(aShapes, aWS);
+    AddWarning(new BOPAlgo_AlertUnableToRepeat(aWS));
+    return myRepeatedShape;
+  }
+  // Get glued shape
+  myRepeatedShape = aGluer.Shape();
+
+  // Update repetition period for the next repetitions
+  myRepeatPeriod[id] += Abs(theTimes) * myRepeatPeriod[id];
+
+  // Update history with the Gluing history
+  BRepTools_History aGluingHistory(aShapes, aGluer);
+  myHistory->Merge(aGluingHistory);
+
+  // Update the map of twins after repetition
+  UpdateTwins(aTranslationHistory, aGluingHistory);
+
+  return myRepeatedShape;
+}
+
+//=======================================================================
+//function : UpdateTwins
+//purpose  : Updates the map of twins after repetition
+//=======================================================================
+void BOPAlgo_MakePeriodic::UpdateTwins(const BRepTools_History& theTranslationHistory,
+                                       const BRepTools_History& theGluingHistory)
+{
+  if (myTwins.IsEmpty())
+    return;
+
+  if (myRepeatedTwins.IsEmpty())
+    myRepeatedTwins = myTwins;
+
+  // New twins
+  TopTools_DataMapOfShapeListOfShape aNewTwinsMap;
+
+  // Fence map to avoid repeated fill for the twins
+  TopTools_MapOfShape aMTwinsDone;
+
+  // Update the map of twins with the new repeated shapes
+  TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itDMap(myRepeatedTwins);
+  for (; itDMap.More(); itDMap.Next())
+  {
+    const TopoDS_Shape& aS = itDMap.Key();
+    aMTwinsDone.Add(aS);
+
+    const TopTools_ListOfShape& aLTwins = itDMap.Value();
+
+    // Check if the twins have not been already processed
+    TopTools_ListIteratorOfListOfShape itLT(aLTwins);
+    for (; itLT.More(); itLT.Next())
+    {
+      if (aMTwinsDone.Contains(itLT.Value()))
+        break;
+    }
+    if (itLT.More())
+      // Group of twins has already been processed
+      continue;
+
+    // All shapes generated from the shape itself and generated
+    // from its twins will be the new twins for the shape
+    TopTools_IndexedMapOfShape aNewGroup;
+    itLT.Initialize(aLTwins);
+
+    for (Standard_Boolean bShape = Standard_True; itLT.More();)
+    {
+      const TopoDS_Shape& aTwin = bShape ? aS : itLT.Value();
+      const TopTools_ListOfShape& aLG = theTranslationHistory.Generated(aTwin);
+      TopTools_ListIteratorOfListOfShape itLG(aLG);
+      for (; itLG.More(); itLG.Next())
+      {
+        const TopoDS_Shape& aG = itLG.Value();
+        const TopTools_ListOfShape& aLM = theGluingHistory.Modified(aG);
+        if (aLM.IsEmpty())
+          aNewGroup.Add(aG);
+        else
+        {
+          TopTools_ListIteratorOfListOfShape itLM(aLM);
+          for (; itLM.More(); itLM.Next())
+            aNewGroup.Add(itLM.Value());
+        }
+      }
+
+      if (bShape)
+        bShape = Standard_False;
+      else
+        itLT.Next();
+    }
+
+    // Associate the twins to each other
+    const Standard_Integer aNbTwins = aNewGroup.Extent();
+    for (Standard_Integer i = 1; i <= aNbTwins; ++i)
+    {
+      TopTools_ListOfShape* pTwins = aNewTwinsMap.Bound(aNewGroup(i), TopTools_ListOfShape());
+      for (Standard_Integer j = 1; j <= aNbTwins; ++j)
+        if (i != j) pTwins->Append(aNewGroup(j));
+    }
+  }
+
+  myRepeatedTwins = aNewTwinsMap;
+}
diff --git a/src/BOPAlgo/BOPAlgo_MakePeriodic.hxx b/src/BOPAlgo/BOPAlgo_MakePeriodic.hxx
new file mode 100644 (file)
index 0000000..8385dff
--- /dev/null
@@ -0,0 +1,603 @@
+// Created on: 2018-03-16
+// Created by: Eugeny MALTCHIKOV
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _BOPAlgo_MakePeriodic_HeaderFile
+#define _BOPAlgo_MakePeriodic_HeaderFile
+
+#include <Standard.hxx>
+#include <Standard_DefineAlloc.hxx>
+#include <Standard_Handle.hxx>
+
+#include <BOPAlgo_Options.hxx>
+#include <BRepTools_History.hxx>
+#include <Standard_Boolean.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopTools_DataMapOfShapeShape.hxx>
+
+//! BOPAlgo_MakePeriodic is the tool for making an arbitrary shape periodic
+//! in 3D space in specified directions.
+//!
+//! Periodicity of the shape means that the shape can be repeated in any
+//! periodic direction any number of times without creation of the new
+//! geometry or splits.
+//!
+//! The idea is to make the shape look identical on the opposite sides of the
+//! periodic directions, so when translating the copy of a shape on the period
+//! there will be no coinciding parts of different dimensions.
+//!
+//! If necessary the algorithm will trim the shape to fit it into the
+//! requested period by splitting it by the planes limiting the shape's
+//! requested period.
+//!
+//! For making the shape periodic in certain direction the algorithm performs
+//! the following steps:
+//! * Creates the copy of the shape and moves it on the period into negative
+//!   side of the requested direction;
+//! * Splits the negative side of the shape by the moved copy, ensuring copying
+//!   of the geometry from positive side to negative;
+//! * Creates the copy of the shape (with already split negative side) and moves
+//!   it on the period into the positive side of the requested direction;
+//! * Splits the positive side of the shape by the moved copy, ensuring copying
+//!   of the geometry from negative side to positive.
+//!
+//! The algorithm also associates the identical (or twin) shapes located
+//! on the opposite sides of the result shape.
+//! Using the *GetTwins()* method it is possible to get the twin shapes from
+//! the opposite sides.
+//!
+//! Algorithm also provides the methods to repeat the periodic shape in
+//! periodic directions. The subsequent repetitions are performed on the
+//! repeated shape, thus repeating the shape two times in X direction will
+//! create result in three shapes (original plus two copies).
+//! Single subsequent repetition will result already in 6 shapes.
+//! The repetitions can be cleared and started over.
+//!
+//! The algorithm supports History of shapes modifications, thus
+//! it is possible to track how the shape has been changed to make it periodic
+//! and what new shapes have been created during repetitions.
+//!
+//! The algorithm supports the parallel processing mode, which allows faster
+//! completion of the operations.
+//!
+//! The algorithm supports the Error/Warning system and returns the following alerts:
+//! - *BOPAlgo_AlertNoPeriodicityRequired* - Error alert is given if no periodicity
+//!                                          has been requested in any direction;
+//! - *BOPAlgo_AlertUnableToTrim* - Error alert is given if the trimming of the shape
+//!                                 for fitting it into requested period has failed;
+//! - *BOPAlgo_AlertUnableToMakeIdentical* - Error alert is given if splitting of the
+//!                                          shape by its moved copies has failed;
+//! - *BOPAlgo_AlertUnableToRepeat* - Warning alert is given if the gluing of the repeated
+//!                                   shapes has failed.
+//!
+//! Example of usage of the algorithm:
+//! ~~~~
+//! TopoDS_Shape aShape = ...;                 // The shape to make periodic
+//! Standard_Boolean bMakeXPeriodic = ...;     // Flag for making or not the shape periodic in X direction
+//! Standard_Real aXPeriod = ...;              // X period for the shape
+//! Standard_Boolean isXTrimmed = ...;         // Flag defining whether it is necessary to trimming
+//!                                            // the shape to fit to X period
+//! Standard_Real aXFirst = ...;               // Start of the X period
+//!                                            // (really necessary only if the trimming is requested)
+//! Standard_Boolean bRunParallel = ...;       // Parallel processing mode or single
+//!
+//! BOPAlgo_MakePeriodic aPeriodicityMaker;                   // Periodicity maker
+//! aPeriodicityMaker.SetShape(aShape);                       // Set the shape
+//! aPeriodicityMaker.MakeXPeriodic(bMakePeriodic, aXPeriod); // Making the shape periodic in X direction
+//! aPeriodicityMaker.SetTrimmed(isXTrimmed, aXFirst);        // Trim the shape to fit X period
+//! aPeriodicityMaker.SetRunParallel(bRunParallel);           // Set the parallel processing mode
+//! aPeriodicityMaker.Perform();                              // Performing the operation
+//!
+//! if (aPeriodicityMaker.HasErrors())                        // Check for the errors
+//! {
+//!   // errors treatment
+//!   Standard_SStream aSStream;
+//!   aPeriodicityMaker.DumpErrors(aSStream);
+//!   return;
+//! }
+//! if (aPeriodicityMaker.HasWarnings())                      // Check for the warnings
+//! {
+//!   // warnings treatment
+//!   Standard_SStream aSStream;
+//!   aPeriodicityMaker.DumpWarnings(aSStream);
+//! }
+//! const TopoDS_Shape& aPeriodicShape = aPeriodicityMaker.Shape(); // Result periodic shape
+//!
+//!
+//! aPeriodicityMaker.XRepeat(2);                                    // Making repetitions
+//! const TopoDS_Shape& aRepeat = aPeriodicityMaker.RepeatedShape(); // Getting the repeated shape
+//! aPeriodicityMaker.ClearRepetitions();                            // Clearing the repetitions
+//! ~~~~
+//!
+class BOPAlgo_MakePeriodic : public BOPAlgo_Options
+{
+public:
+
+  DEFINE_STANDARD_ALLOC
+
+public: //! @name Constructor
+
+  //! Empty constructor
+  BOPAlgo_MakePeriodic() : BOPAlgo_Options()
+  {
+    myRepeatPeriod[0] = myRepeatPeriod[1] = myRepeatPeriod[2] = 0.0;
+  }
+
+
+public: //! @name Setting the shape to make it periodic
+
+  //! Sets the shape to make it periodic.
+  //! @param theShape [in] The shape to make periodic.
+  void SetShape(const TopoDS_Shape& theShape)
+  {
+    myInputShape = theShape;
+  }
+
+
+public: //! @name Definition of the structure to keep all periodicity parameters
+
+  //! Structure to keep all periodicity parameters:
+  struct PeriodicityParams
+  {
+    PeriodicityParams()
+    {
+      Clear();
+    }
+
+    //! Returns all previously set parameters to default values
+    void Clear()
+    {
+      myPeriodic[0] = myPeriodic[1] = myPeriodic[2] = Standard_False;
+      myPeriod[0] = myPeriod[1] = myPeriod[2] = 0.0;
+      myIsTrimmed[0] = myIsTrimmed[1] = myIsTrimmed[2] = Standard_True;
+      myPeriodFirst[0] = myPeriodFirst[1] = myPeriodFirst[2] = 0.0;
+    }
+
+    Standard_Boolean myPeriodic[3];  //!< Array of flags defining whether the shape should be
+                                     //! periodic in XYZ directions
+    Standard_Real myPeriod[3];       //!< Array of XYZ period values. Defining the period for any
+                                     //! direction the corresponding flag for that direction in
+                                     //! myPeriodic should be set to true
+    Standard_Boolean myIsTrimmed[3]; //!< Array of flags defining whether the input shape has to be
+                                     //! trimmed to fit the required period in the required direction
+    Standard_Real myPeriodFirst[3];  //!< Array of start parameters of the XYZ periods: required for trimming
+  };
+
+
+public: //! @name Setters/Getters for periodicity parameters structure
+
+  //! Sets the periodicity parameters.
+  //! @param theParams [in] Periodicity parameters
+  void SetPeriodicityParameters(const PeriodicityParams& theParams)
+  {
+    myPeriodicityParams = theParams;
+  }
+
+  const PeriodicityParams& PeriodicityParameters() const
+  {
+    return myPeriodicityParams;
+  }
+
+
+public: //! @name Methods for setting/getting periodicity info using ID as a direction
+
+  //! Sets the flag to make the shape periodic in specified direction:
+  //! - 0 - X direction;
+  //! - 1 - Y direction;
+  //! - 2 - Z direction.
+  //!
+  //! @param theDirectionID [in] The direction's ID;
+  //! @param theIsPeriodic [in] Flag defining periodicity in given direction;
+  //! @param thePeriod [in] Required period in given direction.
+  void MakePeriodic(const Standard_Integer theDirectionID,
+                    const Standard_Boolean theIsPeriodic,
+                    const Standard_Real thePeriod = 0.0)
+  {
+    Standard_Integer id = ToDirectionID(theDirectionID);
+    myPeriodicityParams.myPeriodic[id] = theIsPeriodic;
+    myPeriodicityParams.myPeriod[id] = theIsPeriodic ? thePeriod : 0.0;
+  }
+
+  //! Returns the info about Periodicity of the shape in specified direction.
+  //! @param theDirectionID [in] The direction's ID.
+  Standard_Boolean IsPeriodic(const Standard_Integer theDirectionID) const
+  {
+    return myPeriodicityParams.myPeriodic[ToDirectionID(theDirectionID)];
+  }
+
+  //! Returns the Period of the shape in specified direction.
+  //! @param theDirectionID [in] The direction's ID.
+  Standard_Real Period(const Standard_Integer theDirectionID) const
+  {
+    Standard_Integer id = ToDirectionID(theDirectionID);
+    return myPeriodicityParams.myPeriodic[id] ? myPeriodicityParams.myPeriod[id] : 0.0;
+  }
+
+
+public: //! @name Named methods for setting/getting info about shape's periodicity
+
+  //! Sets the flag to make the shape periodic in X direction.
+  //! @param theIsPeriodic [in] Flag defining periodicity in X direction;
+  //! @param thePeriod [in] Required period in X direction.
+  void MakeXPeriodic(const Standard_Boolean theIsPeriodic,
+                     const Standard_Real thePeriod = 0.0)
+  {
+    MakePeriodic(0, theIsPeriodic, thePeriod);
+  }
+
+  //! Returns the info about periodicity of the shape in X direction.
+  Standard_Boolean IsXPeriodic() const { return IsPeriodic(0); }
+
+  //! Returns the XPeriod of the shape
+  Standard_Real XPeriod() const { return Period(0); }
+
+  //! Sets the flag to make the shape periodic in Y direction.
+  //! @param theIsPeriodic [in] Flag defining periodicity in Y direction;
+  //! @param thePeriod [in] Required period in Y direction.
+  void MakeYPeriodic(const Standard_Boolean theIsPeriodic,
+                     const Standard_Real thePeriod = 0.0)
+  {
+    MakePeriodic(1, theIsPeriodic, thePeriod);
+  }
+
+  //! Returns the info about periodicity of the shape in Y direction.
+  Standard_Boolean IsYPeriodic() const { return IsPeriodic(1); }
+
+  //! Returns the YPeriod of the shape.
+  Standard_Real YPeriod() const { return Period(1); }
+
+  //! Sets the flag to make the shape periodic in Z direction.
+  //! @param theIsPeriodic [in] Flag defining periodicity in Z direction;
+  //! @param thePeriod [in] Required period in Z direction.
+  void MakeZPeriodic(const Standard_Boolean theIsPeriodic,
+                     const Standard_Real thePeriod = 0.0)
+  {
+    MakePeriodic(2, theIsPeriodic, thePeriod);
+  }
+
+  //! Returns the info about periodicity of the shape in Z direction.
+  Standard_Boolean IsZPeriodic() const { return IsPeriodic(2); }
+
+  //! Returns the ZPeriod of the shape.
+  Standard_Real ZPeriod() const { return Period(2); }
+
+
+public: //! @name Methods for setting/getting trimming info taking Direction ID as a parameter
+
+  //! Defines whether the input shape is already trimmed in specified direction
+  //! to fit the period in this direction.
+  //! Direction is defined by an ID:
+  //! - 0 - X direction;
+  //! - 1 - Y direction;
+  //! - 2 - Z direction.
+  //!
+  //! If the shape is not trimmed it is required to set the first parameter
+  //! of the period in that direction.
+  //! The algorithm will make the shape fit into the period.
+  //!
+  //! Before calling this method, the shape has to be set to be periodic in this direction.
+  //!
+  //! @param theDirectionID [in] The direction's ID;
+  //! @param theIsTrimmed [in] The flag defining trimming of the shape in given direction;
+  //! @param theFirst [in] The first periodic parameter in the given direction.
+  void SetTrimmed(const Standard_Integer theDirectionID,
+                  const Standard_Boolean theIsTrimmed,
+                  const Standard_Real theFirst = 0.0)
+  {
+    Standard_Integer id = ToDirectionID(theDirectionID);
+    if (IsPeriodic(id))
+    {
+      myPeriodicityParams.myIsTrimmed[id] = theIsTrimmed;
+      myPeriodicityParams.myPeriodFirst[id] = !theIsTrimmed ? theFirst : 0.0;
+    }
+  }
+
+  //! Returns whether the input shape was trimmed in the specified direction.
+  //! @param theDirectionID [in] The direction's ID.
+  Standard_Boolean IsInputTrimmed(const Standard_Integer theDirectionID) const
+  {
+    return myPeriodicityParams.myIsTrimmed[ToDirectionID(theDirectionID)];
+  }
+
+  //! Returns the first periodic parameter in the specified direction.
+  //! @param theDirectionID [in] The direction's ID.
+  Standard_Real PeriodFirst(const Standard_Integer theDirectionID) const
+  {
+    Standard_Integer id = ToDirectionID(theDirectionID);
+    return !myPeriodicityParams.myIsTrimmed[id] ? myPeriodicityParams.myPeriodFirst[id] : 0.0;
+  }
+
+
+public: //! @name Named methods for setting/getting trimming info
+
+  //! Defines whether the input shape is already trimmed in X direction
+  //! to fit the X period. If the shape is not trimmed it is required
+  //! to set the first parameter for the X period.
+  //! The algorithm will make the shape fit into the period.
+  //!
+  //! Before calling this method, the shape has to be set to be periodic in this direction.
+  //!
+  //! @param theIsTrimmed [in] Flag defining whether the shape is already trimmed
+  //!                          in X direction to fit the X period;
+  //! @param theFirst [in] The first X periodic parameter.
+  void SetXTrimmed(const Standard_Boolean theIsTrimmed,
+                   const Standard_Boolean theFirst = 0.0)
+  {
+    SetTrimmed(0, theIsTrimmed, theFirst);
+  }
+
+  //! Returns whether the input shape was already trimmed for X period.
+  Standard_Boolean IsInputXTrimmed() const
+  {
+    return IsInputTrimmed(0);
+  }
+
+  //! Returns the first parameter for the X period.
+  Standard_Real XPeriodFirst() const
+  {
+    return PeriodFirst(0);
+  }
+
+  //! Defines whether the input shape is already trimmed in Y direction
+  //! to fit the Y period. If the shape is not trimmed it is required
+  //! to set the first parameter for the Y period.
+  //! The algorithm will make the shape fit into the period.
+  //!
+  //! Before calling this method, the shape has to be set to be periodic in this direction.
+  //!
+  //! @param theIsTrimmed [in] Flag defining whether the shape is already trimmed
+  //!                          in Y direction to fit the Y period;
+  //! @param theFirst [in] The first Y periodic parameter.
+  void SetYTrimmed(const Standard_Boolean theIsTrimmed,
+                   const Standard_Boolean theFirst = 0.0)
+  {
+    SetTrimmed(1, theIsTrimmed, theFirst);
+  }
+
+  //! Returns whether the input shape was already trimmed for Y period.
+  Standard_Boolean IsInputYTrimmed() const
+  {
+    return IsInputTrimmed(1);
+  }
+
+  //! Returns the first parameter for the Y period.
+  Standard_Real YPeriodFirst() const
+  {
+    return PeriodFirst(1);
+  }
+
+  //! Defines whether the input shape is already trimmed in Z direction
+  //! to fit the Z period. If the shape is not trimmed it is required
+  //! to set the first parameter for the Z period.
+  //! The algorithm will make the shape fit into the period.
+  //!
+  //! Before calling this method, the shape has to be set to be periodic in this direction.
+  //!
+  //! @param theIsTrimmed [in] Flag defining whether the shape is already trimmed
+  //!                          in Z direction to fit the Z period;
+  //! @param theFirst [in] The first Z periodic parameter.
+  void SetZTrimmed(const Standard_Boolean theIsTrimmed,
+                   const Standard_Boolean theFirst = 0.0)
+  {
+    SetTrimmed(2, theIsTrimmed, theFirst);
+  }
+
+  //! Returns whether the input shape was already trimmed for Z period.
+  Standard_Boolean IsInputZTrimmed() const
+  {
+    return IsInputTrimmed(2);
+  }
+
+  //! Returns the first parameter for the Z period.
+  Standard_Real ZPeriodFirst() const
+  {
+    return PeriodFirst(2);
+  }
+
+public: //! @name Performing  the operation
+
+  //! Makes the shape periodic in necessary directions
+  Standard_EXPORT void Perform();
+
+
+public: //! @name Using the algorithm to repeat the shape
+
+  //! Performs repetition of the shape in specified direction
+  //! required number of times.
+  //! Negative value of times means that the repetition should
+  //! be perform in negative direction.
+  //! Makes the repeated shape a base for following repetitions.
+  //!
+  //! @param theDirectionID [in] The direction's ID;
+  //! @param theTimes [in] Requested number of repetitions.
+  Standard_EXPORT const TopoDS_Shape& RepeatShape(const Standard_Integer theDirectionID,
+                                                  const Standard_Integer theTimes);
+
+  //! Repeats the shape in X direction specified number of times.
+  //! Negative value of times means that the repetition should be
+  //! perform in negative X direction.
+  //! Makes the repeated shape a base for following repetitions.
+  //!
+  //! @param theTimes [in] Requested number of repetitions.
+  const TopoDS_Shape& XRepeat(const Standard_Integer theTimes)
+  {
+    return RepeatShape(0, theTimes);
+  }
+
+  //! Repeats the shape in Y direction specified number of times.
+  //! Negative value of times means that the repetition should be
+  //! perform in negative Y direction.
+  //! Makes the repeated shape a base for following repetitions.
+  //!
+  //! @param theTimes [in] Requested number of repetitions.
+  const TopoDS_Shape& YRepeat(const Standard_Integer theTimes)
+  {
+    return RepeatShape(1, theTimes);
+  }
+
+  //! Repeats the shape in Z direction specified number of times.
+  //! Negative value of times means that the repetition should be
+  //! perform in negative Z direction.
+  //! Makes the repeated shape a base for following repetitions.
+  //!
+  //! @param theTimes [in] Requested number of repetitions.
+  const TopoDS_Shape& ZRepeat(const Standard_Integer theTimes)
+  {
+    return RepeatShape(2, theTimes);
+  }
+
+
+public: //! @name Starting the repetitions over
+
+  //! Returns the repeated shape
+  const TopoDS_Shape& RepeatedShape() const { return myRepeatedShape; }
+
+  //! Clears all performed repetitions.
+  //! The next repetition will be performed on the base shape.
+  void ClearRepetitions()
+  {
+    myRepeatPeriod[0] = myRepeatPeriod[1] = myRepeatPeriod[2] = 0.0;
+    myRepeatedShape.Nullify();
+    myRepeatedTwins.Clear();
+    if (!myHistory.IsNull())
+    {
+      myHistory->Clear();
+      if (!mySplitHistory.IsNull())
+        myHistory->Merge(mySplitHistory);
+    }
+  }
+
+public: //! @name Obtaining the result shape
+
+  //! Returns the resulting periodic shape
+  const TopoDS_Shape& Shape() const { return myShape; }
+
+
+public: //! @name Getting the identical shapes
+
+  //! Returns the identical shapes for the given shape located
+  //! on the opposite periodic side.
+  //! Returns empty list in case the shape has no twin.
+  //!
+  //! @param theS [in] Shape to get the twins for.
+  const TopTools_ListOfShape& GetTwins(const TopoDS_Shape& theS) const
+  {
+    static TopTools_ListOfShape empty;
+    const TopTools_ListOfShape* aTwins =
+      myRepeatedTwins.IsEmpty() ? myTwins.Seek(theS) : myRepeatedTwins.Seek(theS);
+    return (aTwins ? *aTwins : empty);
+  }
+
+
+public: //! @name Getting the History of the algorithm
+
+  //! Returns the History of the algorithm
+  const Handle(BRepTools_History)& History() const
+  {
+    return myHistory;
+  }
+
+public: //! @name Clearing the algorithm from previous runs
+
+  //! Clears the algorithm from previous runs
+  void Clear()
+  {
+    BOPAlgo_Options::Clear();
+    myPeriodicityParams.Clear();
+    myShape.Nullify();
+    if (!mySplitHistory.IsNull())
+      mySplitHistory->Clear();
+    if (!myHistory.IsNull())
+      myHistory->Clear();
+
+    ClearRepetitions();
+  }
+
+
+public: //! @name Conversion of the integer to ID of periodic direction
+
+  //! Converts the integer to ID of periodic direction
+  static Standard_Integer ToDirectionID(const Standard_Integer theDirectionID)
+  {
+    return Abs(theDirectionID % 3);
+  }
+
+
+protected: //! @name Protected methods performing the operation
+
+  //! Checks the validity of input data
+  Standard_EXPORT void CheckData();
+
+  //! Trims the shape to fit to the periodic bounds
+  Standard_EXPORT void Trim();
+
+  //! Makes the shape identical on opposite sides
+  Standard_EXPORT void MakeIdentical();
+
+  //! Splits the negative side of the shape with the geometry
+  //! located on the positive side copying the geometry from
+  //! positive side to the negative.
+  Standard_EXPORT void SplitNegative();
+
+  //! Splits the positive side of the shape with the geometry
+  //! located on the negative side of the shape.
+  //! Ensures that the geometries on the opposite sides will
+  //! be identical.
+  //! Associates the identical opposite sub-shapes.
+  Standard_EXPORT void SplitPositive();
+
+  //! Splits the shape by the given tools, copying the geometry of coinciding
+  //! parts from the given tools to the split shape.
+  //! @param theTools [in] The tools to split the shape and take the geometry
+  //!                      for coinciding parts.
+  //! @param theSplitShapeHistory [out] The history of shape split
+  //! @param theSplitToolsHistory [out] The history of tools modifications during the split
+  Standard_EXPORT void SplitShape(const TopTools_ListOfShape& theTools,
+                                  Handle(BRepTools_History) theSplitShapeHistory = NULL,
+                                  Handle(BRepTools_History) theSplitToolsHistory = NULL);
+
+  //! Updates the map of twins after periodic shape repetition.
+  //! @param theTranslationHistory [in] The history of translation of the periodic shape.
+  //! @param theGluingHistory [in] The history of gluing of the repeated shapes.
+  Standard_EXPORT void UpdateTwins(const BRepTools_History& theTranslationHistory,
+                                   const BRepTools_History& theGluingHistory);
+
+
+protected: //! @name Fields
+
+  // Inputs
+  TopoDS_Shape myInputShape;         //!< Input shape to make periodic
+
+  PeriodicityParams myPeriodicityParams; //!< Periodicity parameters
+
+  // Results
+  TopoDS_Shape myShape;                //!< Resulting periodic shape (base for repetitions)
+  TopoDS_Shape myRepeatedShape;        //!< Resulting shape after making repetitions of the base
+  Standard_Real myRepeatPeriod[3];     //!< XYZ repeat period
+  TopTools_DataMapOfShapeListOfShape myRepeatedTwins; //!< Map of associations of the identical sub-shapes
+                                                      //! after repetition of the periodic shape
+
+  // Twins
+  TopTools_DataMapOfShapeListOfShape myTwins; //!< Map of associations of the identical sub-shapes
+                                              //! located on the opposite sides of the shape
+
+  // History
+  Handle(BRepTools_History) mySplitHistory;  //!< Split history - history of shapes modification
+                                             //! after the split for making the shape periodic
+  Handle(BRepTools_History) myHistory;       //!< Final history of shapes modifications
+                                             //! (to include the history of shape repetition)
+
+};
+
+#endif // _BOPAlgo_MakePeriodic_HeaderFile
index 421c9fa..c48fca5 100644 (file)
@@ -173,22 +173,6 @@ void BOPAlgo_PaveFiller::SetSectionAttribute
   mySectionAttribute = theSecAttr;
 }
 //=======================================================================
-//function : SetArguments
-//purpose  : 
-//=======================================================================
-void BOPAlgo_PaveFiller::SetArguments(const TopTools_ListOfShape& theLS)
-{
-  myArguments=theLS;
-}
-//=======================================================================
-//function : Arguments
-//purpose  : 
-//=======================================================================
-const TopTools_ListOfShape& BOPAlgo_PaveFiller::Arguments()const
-{
-  return myArguments;
-}
-//=======================================================================
 // function: Init
 // purpose: 
 //=======================================================================
index 8baf0fa..f93d867 100644 (file)
@@ -125,9 +125,23 @@ public:
   
   Standard_EXPORT const BOPDS_PIterator& Iterator();
   
-  Standard_EXPORT void SetArguments (const TopTools_ListOfShape& theLS);
-  
-  Standard_EXPORT const TopTools_ListOfShape& Arguments() const;
+  //! Sets the arguments for operation
+  void SetArguments (const TopTools_ListOfShape& theLS)
+  {
+    myArguments = theLS;
+  }
+
+  //! Adds the argument for operation
+  void AddArgument(const TopoDS_Shape& theShape)
+  {
+    myArguments.Append(theShape);
+  }
+
+  //! Returns the list of arguments
+  const TopTools_ListOfShape& Arguments() const
+  {
+    return myArguments;
+  }
   
   Standard_EXPORT const Handle(IntTools_Context)& Context();
   
index 6f93a9c..c0bea0d 100644 (file)
@@ -17,6 +17,8 @@
 #include <BOPAlgo_PaveFiller.hxx>
 #include <BOPAlgo_Alerts.hxx>
 
+#include <TopoDS_Iterator.hxx>
+
 //=======================================================================
 //function : 
 //purpose  : 
@@ -98,3 +100,31 @@ void BOPAlgo_Splitter::Perform()
   myEntryPoint = 1;
   PerformInternal(*pPF);
 }
+
+//=======================================================================
+//function : BuildResult
+//purpose  : 
+//=======================================================================
+void BOPAlgo_Splitter::BuildResult(const TopAbs_ShapeEnum theType)
+{
+  BOPAlgo_Builder::BuildResult(theType);
+
+  if (theType == TopAbs_COMPOUND)
+  {
+    // The method is called for the last time for this operation.
+    // If there is only one argument shape and it has been modified into
+    // a single shape, or has not been modified at all, the result shape
+    // has to be overwritten to avoid the unnecessary enclosure into compound.
+    if (myArguments.Extent() == 1)
+    {
+      TopoDS_Iterator it(myShape);
+      if (it.More())
+      {
+        const TopoDS_Shape& aSFirst = it.Value();
+        it.Next();
+        if (!it.More())
+          myShape = aSFirst;
+      }
+    }
+  }
+}
index 62a8789..67189f3 100644 (file)
@@ -66,6 +66,12 @@ protected:
 
   //! Checks the input data
   Standard_EXPORT virtual void CheckData() Standard_OVERRIDE;
+
+  //! Adds images of the argument shapes into result.
+  //! When called the for the last time (for compound) it rebuilds the result
+  //! shape to avoid multiple enclosure into compounds.
+  Standard_EXPORT virtual void BuildResult(const TopAbs_ShapeEnum theType) Standard_OVERRIDE;
+
 };
 
 #endif // _BOPAlgo_Splitter_HeaderFile
index e2a3151..50b6cec 100644 (file)
@@ -27,6 +27,8 @@ BOPAlgo_CheckResult.cxx
 BOPAlgo_CheckResult.hxx
 BOPAlgo_CheckStatus.hxx
 BOPAlgo_ListOfCheckResult.hxx
+BOPAlgo_MakePeriodic.cxx
+BOPAlgo_MakePeriodic.hxx
 BOPAlgo_MakerVolume.cxx
 BOPAlgo_MakerVolume.hxx
 BOPAlgo_MakerVolume.lxx
index 790495e..873c184 100644 (file)
@@ -57,6 +57,7 @@ void  BOPTest::AllCommands(Draw_Interpretor& theCommands)
   BOPTest::CellsCommands     (theCommands);
   BOPTest::UtilityCommands   (theCommands);
   BOPTest::RemoveFeaturesCommands(theCommands);
+  BOPTest::PeriodicityCommands(theCommands);
 }
 //=======================================================================
 //function : Factory
index 20fbd91..abf338d 100644 (file)
@@ -60,6 +60,8 @@ public:
 
   Standard_EXPORT static void RemoveFeaturesCommands (Draw_Interpretor& aDI);
 
+  Standard_EXPORT static void PeriodicityCommands (Draw_Interpretor& aDI);
+
   //! Prints errors and warnings if any and draws attached shapes 
   //! if flag BOPTest_Objects::DrawWarnShapes() is set
   Standard_EXPORT static void ReportAlerts (const Handle(Message_Report)& theReport);
diff --git a/src/BOPTest/BOPTest_PeriodicityCommands.cxx b/src/BOPTest/BOPTest_PeriodicityCommands.cxx
new file mode 100644 (file)
index 0000000..571745e
--- /dev/null
@@ -0,0 +1,293 @@
+// Created on: 03/19/2018
+// Created by: Eugeny MALTCHIKOV
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <BOPTest.hxx>
+
+#include <BOPAlgo_MakePeriodic.hxx>
+
+#include <BOPTest_DrawableShape.hxx>
+#include <BOPTest_Objects.hxx>
+
+#include <BRep_Builder.hxx>
+
+#include <BRepTest_Objects.hxx>
+
+#include <DBRep.hxx>
+#include <Draw.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
+
+static Standard_Integer MakePeriodic(Draw_Interpretor&, Standard_Integer, const char**);
+static Standard_Integer GetTwins(Draw_Interpretor&, Standard_Integer, const char**);
+static Standard_Integer RepeatShape(Draw_Interpretor&, Standard_Integer, const char**);
+static Standard_Integer ClearRepetitions(Draw_Interpretor&, Standard_Integer, const char**);
+
+namespace
+{
+  static BOPAlgo_MakePeriodic ThePeriodicityMaker;
+}
+
+//=======================================================================
+//function : PeriodicityCommands
+//purpose  : 
+//=======================================================================
+void BOPTest::PeriodicityCommands(Draw_Interpretor& theCommands)
+{
+  static Standard_Boolean done = Standard_False;
+  if (done) return;
+  done = Standard_True;
+  // Chapter's name
+  const char* group = "BOPTest commands";
+  // Commands
+  theCommands.Add("makeperiodic", "makeperiodic result shape [-x/y/z period [-trim first]]\n"
+                  "\t\tMake the shape periodic in the required directions.\n"
+                  "\t\tresult        - resulting periodic shape;\n"
+                  "\t\t-x/y/z period - option to make the shape periodic in X, Y or Z\n "
+                  "\t\t                direction with the given period;\n"
+                  "\t\t-trim first   - option to trim the shape to fit the required period,\n"
+                  "\t\t                starting the period in first.",
+                  __FILE__, MakePeriodic, group);
+
+  theCommands.Add("periodictwins", "periodictwins twins shape\n"
+                  "\t\tReturns the twins for the shape located on the opposite side of the periodic shape.",
+                  __FILE__, GetTwins, group);
+
+  // Repetition commands
+  theCommands.Add("repeatshape", "repeatshape result -x/y/z times\n"
+                  "\t\tRepeats the periodic shape in periodic directions required number of times.\n"
+                  "\t\tresult       - resulting shape;\n"
+                  "\t\t-x/y/z times - direction for repetition and number of repetitions.",
+                  __FILE__, RepeatShape, group);
+
+  theCommands.Add("clearrepetitions", "clearrepetitions\n"
+                  "\t\tClears all previous repetitions of the periodic shape (used without any arguments).",
+                  __FILE__, ClearRepetitions, group);
+}
+
+//=======================================================================
+//function : MakePeriodic
+//purpose  : 
+//=======================================================================
+Standard_Integer MakePeriodic(Draw_Interpretor& theDI,
+                              Standard_Integer  theArgc,
+                              const char ** theArgv)
+{
+  if (theArgc < 5)
+  {
+    theDI.PrintHelp(theArgv[0]);
+    return 1;
+  }
+
+  // Get the shape to make periodic
+  TopoDS_Shape aShape = DBRep::Get(theArgv[2]);
+  if (aShape.IsNull())
+  {
+    theDI << "Error: " << theArgv[2] << " is a null shape.\n";
+    return 1;
+  }
+
+  ThePeriodicityMaker.Clear();
+  ThePeriodicityMaker.SetShape(aShape);
+
+  for (Standard_Integer i = 3; i < theArgc;)
+  {
+    // Get periodicity
+    Standard_Integer iDir = i;
+
+    Standard_Integer aDirID = -1;
+    if (!strcasecmp(theArgv[i], "-x"))
+      aDirID = 0;
+    else if (!strcasecmp(theArgv[i], "-y"))
+      aDirID = 1;
+    else if (!strcasecmp(theArgv[i], "-z"))
+      aDirID = 2;
+    else
+    {
+      theDI << theArgv[i] << " - Invalid key\n";
+      return 1;
+    }
+
+    char cDirName[2];
+    sprintf(cDirName, "%c", theArgv[iDir][1]);
+
+    if (theArgc == (i + 1))
+    {
+      theDI << "Period for " << cDirName << " direction is not set\n";
+      return 1;
+    }
+
+    Standard_Real aPeriod = Draw::Atof(theArgv[++i]);
+
+    ThePeriodicityMaker.MakePeriodic(aDirID, Standard_True, aPeriod);
+
+    ++i;
+    if (theArgc > i + 1)
+    {
+      // Check if trimming is necessary
+      if (!strcmp(theArgv[i], "-trim"))
+      {
+        if (theArgc == (i + 1))
+        {
+          theDI << "Trim bounds for " << cDirName << " direction are not set\n";
+          return 1;
+        }
+        Standard_Real aFirst = Draw::Atof(theArgv[++i]);
+
+        ThePeriodicityMaker.SetTrimmed(aDirID, Standard_False, aFirst);
+        ++i;
+      }
+    }
+  }
+
+  ThePeriodicityMaker.SetRunParallel(BOPTest_Objects::RunParallel());
+
+  // Perform operation
+  ThePeriodicityMaker.Perform();
+
+  // Print Error/Warning messages
+  BOPTest::ReportAlerts(ThePeriodicityMaker.GetReport());
+
+  // Set the history of the operation in session
+  BRepTest_Objects::SetHistory(ThePeriodicityMaker.History());
+
+  if (ThePeriodicityMaker.HasErrors())
+    return 0;
+
+  // Draw the result shape
+  const TopoDS_Shape& aResult = ThePeriodicityMaker.Shape();
+  DBRep::Set(theArgv[1], aResult);
+
+  return 0;
+}
+
+//=======================================================================
+//function : GetTwin
+//purpose  : 
+//=======================================================================
+Standard_Integer GetTwins(Draw_Interpretor& theDI,
+                          Standard_Integer  theArgc,
+                          const char ** theArgv)
+{
+  if (theArgc != 3)
+  {
+    theDI.PrintHelp(theArgv[0]);
+    return 1;
+  }
+
+  // Get the shape to find twins
+  TopoDS_Shape aShape = DBRep::Get(theArgv[2]);
+  if (aShape.IsNull())
+  {
+    theDI << "Error: " << theArgv[2] << " is a null shape.\n";
+    return 1;
+  }
+
+  const TopTools_ListOfShape& aTwins = ThePeriodicityMaker.GetTwins(aShape);
+
+  TopoDS_Shape aCTwins;
+  if (aTwins.IsEmpty())
+    theDI << "No twins for the shape.\n";
+  else if (aTwins.Extent() == 1)
+    aCTwins = aTwins.First();
+  else
+  {
+    BRep_Builder().MakeCompound(TopoDS::Compound(aCTwins));
+    for (TopTools_ListIteratorOfListOfShape it(aTwins); it.More(); it.Next())
+      BRep_Builder().Add(aCTwins, it.Value());
+  }
+
+  DBRep::Set(theArgv[1], aCTwins);
+
+  return 0;
+}
+
+//=======================================================================
+//function : RepeatShape
+//purpose  : 
+//=======================================================================
+Standard_Integer RepeatShape(Draw_Interpretor& theDI,
+                             Standard_Integer  theArgc,
+                             const char ** theArgv)
+{
+  if (theArgc < 4)
+  {
+    theDI.PrintHelp(theArgv[0]);
+    return 1;
+  }
+
+  for (Standard_Integer i = 2; i < theArgc; ++i)
+  {
+    Standard_Integer aDirID = -1;
+    if (!strcasecmp(theArgv[i], "-x"))
+      aDirID = 0;
+    else if (!strcasecmp(theArgv[i], "-y"))
+      aDirID = 1;
+    else if (!strcasecmp(theArgv[i], "-z"))
+      aDirID = 2;
+    else
+    {
+      theDI << theArgv[i] << " - Invalid key\n";
+      return 1;
+    }
+
+    char cDirName[2];
+    sprintf(cDirName, "%c", theArgv[i][1]);
+
+    Standard_Integer aTimes = 0;
+    if (theArgc > i + 1)
+      aTimes = Draw::Atoi(theArgv[++i]);
+
+    if (aTimes == 0)
+    {
+      theDI << "Number of repetitions for " << cDirName << " direction is not set\n";
+      return 1;
+    }
+
+    ThePeriodicityMaker.RepeatShape(aDirID, aTimes);
+  }
+
+  // Print Error/Warning messages
+  BOPTest::ReportAlerts(ThePeriodicityMaker.GetReport());
+
+  // Set the history of the operation in session
+  BRepTest_Objects::SetHistory(ThePeriodicityMaker.History());
+
+  if (ThePeriodicityMaker.HasErrors())
+    return 0;
+
+  // Draw the result shape
+  const TopoDS_Shape& aResult = ThePeriodicityMaker.RepeatedShape();
+  DBRep::Set(theArgv[1], aResult);
+
+  return 0;
+}
+
+//=======================================================================
+//function : ClearRepetitions
+//purpose  : 
+//=======================================================================
+Standard_Integer ClearRepetitions(Draw_Interpretor&,
+                                  Standard_Integer,
+                                  const char **)
+{
+  // Clear all previous repetitions
+  ThePeriodicityMaker.ClearRepetitions();
+
+  // Set the history of the operation in session
+  BRepTest_Objects::SetHistory(ThePeriodicityMaker.History());
+
+  return 0;
+}
index 2c97830..a2ceac1 100755 (executable)
@@ -11,6 +11,7 @@ BOPTest_Objects.cxx
 BOPTest_Objects.hxx
 BOPTest_OptionCommands.cxx
 BOPTest_PartitionCommands.cxx
+BOPTest_PeriodicityCommands.cxx
 BOPTest_TolerCommands.cxx
 BOPTest_DebugCommands.cxx
 BOPTest_CellsCommands.cxx
index 4a295c5..b90dc67 100644 (file)
@@ -30,4 +30,5 @@
 030 history
 031 removefeatures
 032 simplify
-033 opensolid
\ No newline at end of file
+033 opensolid
+034 periodicity
\ No newline at end of file
diff --git a/tests/boolean/periodicity/A1 b/tests/boolean/periodicity/A1
new file mode 100644 (file)
index 0000000..d23cc93
--- /dev/null
@@ -0,0 +1,15 @@
+box b 10 10 10
+
+# make the box periodic
+makeperiodic bp b -x 5 -trim 2 -y 8 -trim 1 -z 12 -trim -1
+
+checkshape bp
+checknbshapes bp -vertex 8 -edge 12 -wire 6 -face 6 -shell 1 -solid 1 -t
+checkprops bp -s 340 -v 400
+
+# repeat the shape
+repeatshape result -x 5 -y 5 -z 5
+
+checkshape result
+checknbshapes result -vertex 588 -edge 1302 -wire 936 -face 936 -shell 216 -solid 216 -t
+checkprops result -s 73440 -v 86400
diff --git a/tests/boolean/periodicity/A2 b/tests/boolean/periodicity/A2
new file mode 100644 (file)
index 0000000..bfca7bf
--- /dev/null
@@ -0,0 +1,80 @@
+polyline p 0 0 0 10 0 0 10 0 10 5 0 10 5 0 5 0 0 5 0 0 0
+mkplane f p
+prism s f 0 5 0
+
+# make the shape periodic
+makeperiodic sp s -x 10 -z 10 -y 5
+
+checkshape sp
+checknbshapes sp -vertex 16 -edge 24 -wire 10 -face 10 -shell 1 -solid 1 -t
+checkprops sp -s 350 -v 375
+
+# get history of the operation
+savehistory h
+
+# check modification of the bottom and side faces
+explode s f
+modified m h s_1
+
+checknbshapes m -vertex 6 -edge 7 -wire 2 -face 2 -t
+explode m f
+periodictwins t1 m_1
+periodictwins t2 m_2
+
+compound t1 t2 t
+checknbshapes t -face 1 -t
+
+
+explode s f
+modified m h s_2
+
+checknbshapes m -vertex 6 -edge 7 -wire 2 -face 2 -t
+explode m f
+periodictwins t1 m_1
+periodictwins t2 m_2
+
+compound t1 t2 t
+checknbshapes t -face 1 -t
+
+
+# repeat the shape
+repeatshape res -x 1 -y 2
+
+checkshape res
+checknbshapes res -vertex 56 -edge 102 -wire 53 -face 53 -shell 6 -solid 6 -t
+checkprops res -s 2100 -v 2250
+
+
+# get repetition history
+savehistory h
+
+# get generations of bottom face
+generated g h s_1
+
+checkshape g
+checknbshapes g -vertex 20 -edge 31 -wire 12 -face 12 -t
+checkprops g -s 300
+
+# get generations of the top face
+generated g h s_3
+
+checkshape g
+checknbshapes g -vertex 16 -edge 20 -wire 6 -face 6 -t
+checkprops g -s 150
+
+foreach f [explode g f] {
+  periodictwins t $f
+  checkshape t
+  checknbshapes t -wire 11 -face 11 -t
+  checkprops t -s 275
+}
+
+# clear repetitions
+clearrepetitions
+
+savehistory h
+
+# now generated for bottom face should be empty
+if {![regexp "No shapes were generated from the shape." [generated g1 h s_1]]} {
+  puts "Error: history is not cleared"
+}
diff --git a/tests/boolean/periodicity/A3 b/tests/boolean/periodicity/A3
new file mode 100644 (file)
index 0000000..142b43d
--- /dev/null
@@ -0,0 +1,14 @@
+box b 100 100 1
+
+pcylinder c 2 2
+ttranslate c 2.5 2.5 0
+
+makeperiodic p c -x 5 -trim 0 -y 5 -trim 0
+
+repeatshape r -x 19 -y 19
+
+bcut result b r
+
+checkshape result
+checknbshapes result -vertex 808 -edge 1212 -wire 1206 -face 406 -shell 1 -solid 1 -t
+checkprops result -s 15373.5 -v 4973.45
diff --git a/tests/boolean/periodicity/A4 b/tests/boolean/periodicity/A4
new file mode 100644 (file)
index 0000000..3826d39
--- /dev/null
@@ -0,0 +1,13 @@
+box b 5 5 1
+pcylinder c 2 2
+ttranslate c 2.5 2.5 0
+
+bcut p b c
+
+makeperiodic p p -x 5 -y 5
+
+repeatshape result -x -9 -x 1 -y -9 -y 1
+
+checkshape result
+checknbshapes result -vertex 1682 -edge 3321 -wire 2840 -face 2040 -shell 400 -solid 400 -t
+checkprops result -s  22973.5 -v 4973.45
diff --git a/tests/boolean/periodicity/A5 b/tests/boolean/periodicity/A5
new file mode 100644 (file)
index 0000000..581aa61
--- /dev/null
@@ -0,0 +1,39 @@
+box b 10 10 10
+copy b bf
+foreach e [explode b e] {
+  nurbsconvert ne $e
+  eval mkvolume s ne [explode bf f]
+
+  savehistory mv_hist
+  modified m mv_hist ne
+
+  mkcurve cur m
+  if {![regexp "BSplineCurve" [dump cur]]} {
+    puts "Error: Boolean Operation took the second face"
+  }
+
+  # make the shape periodic in all directions
+  makeperiodic res s -x 10 -y 10 -z 10
+
+  savehistory h
+  modified me h m
+
+  # get twins for the edge
+  periodictwins twins me
+
+  checknbshapes twins -edge 2 -m "Periodic twins" -t
+  mkcurve c_me me
+  if {![regexp {Basis curve :\n([^ \n]*)} [dump c_me] full e_type]} {
+    puts "Error: Unable to get the type"
+  }
+
+  foreach t [explode twins e] {
+    mkcurve c_$t $t
+    if {![regexp {Basis curve :\n([^ \n]*)} [dump c_$t] full t_type]} {
+      puts "Error: Unable to get the type"
+    }
+    if {$e_type != $t_type} {
+      puts "Error: Twins have different geometries"
+    }
+  }
+}
diff --git a/tests/boolean/periodicity/A6 b/tests/boolean/periodicity/A6
new file mode 100644 (file)
index 0000000..29f035b
--- /dev/null
@@ -0,0 +1,62 @@
+box b 10 10 10
+
+foreach bf [explode b f] {
+  # replace face with the nurbs one
+  nurbsconvert nf $bf
+  eval mkvolume s nf [explode b f]
+
+  savehistory mv_hist
+  modified m mv_hist nf
+
+  mksurface surf m
+  if {![regexp "BSplineSurface" [dump surf]]} {
+    puts "Error: Boolean Operation took the second face"
+  }
+
+  # make the shape periodic in all directions
+  makeperiodic res s -x 10 -y 10 -z 10
+
+  savehistory h
+  modified mf h m
+
+  mksurface s_mf mf
+
+  # get twins for the face
+  periodictwins tf mf
+
+  checknbshapes tf -face 1 -m "Periodic twins" -t
+
+  mksurface s_tf tf
+
+  if {![regexp {\*\n([^ \n]*)} [dump s_mf] full mf_type]} {
+    puts "Error: Unable to get the type"
+  }
+  if {![regexp {\*\n([^ \n]*)} [dump s_tf] full tf_type]} {
+    puts "Error: Unable to get the type"
+  }
+
+  if {$mf_type != $tf_type} {
+    puts "Error: Twins have different geometries"
+  }
+
+  # get twins for edges of the face
+  foreach e [explode mf e] {
+    periodictwins twins $e
+    checknbshapes twins -edge 2 -m "Periodic twins" -t
+    mkcurve c_$e $e
+    if {![regexp {Basis curve :\n([^ \n]*)} [dump c_$e] full e_type]} {
+      puts "Error: Unable to get the type"
+    }
+
+    foreach t [explode twins e] {
+      mkcurve c_$t $t
+      if {![regexp {Basis curve :\n([^ \n]*)} [dump c_$t] full t_type]} {
+        puts "Error: Unable to get the type"
+      }
+      if {$e_type != $t_type} {
+        puts "Error: Twins have different geometries"
+      }
+    }
+  }
+}
+
index f4d0770..8c5d900 100644 (file)
@@ -18,11 +18,12 @@ baddobjects f
 baddtools v
 bfillds
 bsplit r
-explode r f
+
+checknbshapes r -vertex 3 -edge 4 -wire 1 -face 1
 
 # perform unification of the seam edge:
 # the split vertex should be removed
-unifysamedom result r_1
+unifysamedom result r
 
 checkshape result
 checkprops result -equal f