0031836: Modeling Algorithms - Boolean cut failed between a Cone and a Torus
[occt.git] / src / BOPAlgo / BOPAlgo_MakePeriodic.cxx
1 // Created on: 2018-03-16
2 // Created by: Eugeny MALTCHIKOV
3 // Copyright (c) 2018 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <BOPAlgo_MakePeriodic.hxx>
17
18 #include <BOPAlgo_Alerts.hxx>
19
20 #include <Bnd_Box.hxx>
21
22 #include <BOPAlgo_Builder.hxx>
23 #include <BOPAlgo_PaveFiller.hxx>
24
25 #include <BRepAlgoAPI_Common.hxx>
26 #include <BRepAlgoAPI_Splitter.hxx>
27
28 #include <BRepBndLib.hxx>
29
30 #include <BRepBuilderAPI_Transform.hxx>
31
32 #include <BRepPrimAPI_MakeBox.hxx>
33
34 #include <gp_Pln.hxx>
35
36 #include <Precision.hxx>
37
38 #include <TopoDS.hxx>
39 #include <TopoDS_Compound.hxx>
40
41 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
42
43 // Periodic/Trim/Repeat directions
44 static const gp_Dir MY_DIRECTIONS[3] = { gp::DX(),
45                                          gp::DY(),
46                                          gp::DZ() };
47
48 //=======================================================================
49 //function : Perform
50 //purpose  : Performs the operation
51 //=======================================================================
52 void BOPAlgo_MakePeriodic::Perform()
53 {
54   // Check the validity of input data
55   CheckData();
56   if (HasErrors())
57     return;
58
59   // Trim the shape to fit to the required period in
60   // required periodic directions
61   Trim();
62   if (HasErrors())
63     return;
64
65   // Make the shape identical on the opposite sides in
66   // required periodic directions
67   MakeIdentical();
68   if (HasErrors())
69     return;
70 }
71
72 //=======================================================================
73 //function : CheckData
74 //purpose  : Checks the validity of input data
75 //=======================================================================
76 void BOPAlgo_MakePeriodic::CheckData()
77 {
78   if ( (!IsXPeriodic() || XPeriod() < Precision::Confusion())
79     && (!IsYPeriodic() || YPeriod() < Precision::Confusion())
80     && (!IsZPeriodic() || ZPeriod() < Precision::Confusion()))
81   {
82     // Add error informing the user that no periodicity is required
83     // or no valid period is set.
84
85     AddError(new BOPAlgo_AlertNoPeriodicityRequired());
86     return;
87   }
88 }
89
90 //=======================================================================
91 //function : AddToShape
92 //purpose  : Adds the shape <theWhat> to the shape <theWhere>
93 //=======================================================================
94 static void AddToShape(const TopoDS_Shape& theWhat,
95                        TopoDS_Shape& theWhere)
96 {
97   if (theWhere.IsNull())
98     BRep_Builder().MakeCompound(TopoDS::Compound(theWhere));
99   BRep_Builder().Add(theWhere, theWhat);
100 }
101 //=======================================================================
102 //function : AddToShape
103 //purpose  : Adds the shape in the list <theLWhat> to the shape <theWhere>
104 //=======================================================================
105 static void AddToShape(const TopTools_ListOfShape& theLWhat,
106                        TopoDS_Shape& theWhere)
107 {
108   TopTools_ListIteratorOfListOfShape it(theLWhat);
109   for (; it.More(); it.Next())
110     AddToShape(it.Value(), theWhere);
111 }
112
113 //=======================================================================
114 //function : Trim
115 //purpose  : Make the trim of the shape to fit to the periodic bounds.
116 //=======================================================================
117 void BOPAlgo_MakePeriodic::Trim()
118 {
119   // Check if trim is required at all
120   if (IsInputXTrimmed() &&
121       IsInputYTrimmed() &&
122       IsInputZTrimmed())
123       return;
124
125   // Compute bounding box for the shape to use it as a starting
126   // volume for trimming. If required, the volume will be modified
127   // to the requested trimming size in requested directions.
128   Bnd_Box aBox;
129   BRepBndLib::Add(myInputShape, aBox);
130   // Enlarge box to avoid overlapping with the shape
131   aBox.Enlarge(0.1 * sqrt(aBox.SquareExtent()));
132
133   // Get Corner points of the bounding box
134   gp_Pnt aPMin = aBox.CornerMin();
135   gp_Pnt aPMax = aBox.CornerMax();
136
137   // Update corner points according to the requested trim parameters
138   for (Standard_Integer i = 0; i < 3; ++i)
139   {
140     if (IsInputTrimmed(i))
141       continue;
142
143     aPMin.SetCoord(i + 1, PeriodFirst(i));
144     aPMax.SetCoord(i + 1, PeriodFirst(i) + Period(i));
145   }
146
147   // Build Trimming solid using corner points
148   BRepPrimAPI_MakeBox aMBox(aPMin, aPMax);
149   const TopoDS_Shape& aTrimBox = aMBox.Solid();
150
151   // Perform trimming of the shape by solid
152   BRepAlgoAPI_Common aCommon;
153   // Set Object
154   TopTools_ListOfShape anObj;
155   anObj.Append(myInputShape);
156   aCommon.SetArguments(anObj);
157   // Set Tool
158   TopTools_ListOfShape aTool;
159   aTool.Append(aTrimBox);
160   aCommon.SetTools(aTool);
161   // Set the parallel processing mode
162   aCommon.SetRunParallel(myRunParallel);
163   // Build
164   aCommon.Build();
165   if (aCommon.HasErrors())
166   {
167     // Unable to trim the shape
168     // Merge errors from Common operation
169     myReport->Merge(aCommon.GetReport());
170     // Add new error saving the shapes for analysis
171     TopoDS_Compound aWS;
172     AddToShape(myInputShape, aWS);
173     AddToShape(aTrimBox, aWS);
174     AddError(new BOPAlgo_AlertUnableToTrim(aWS));
175     return;
176   }
177   // Get the trimmed shape
178   myShape = aCommon.Shape();
179   // Fill the History for the object only
180   mySplitHistory = new BRepTools_History();
181   mySplitHistory->Merge(anObj, aCommon);
182 }
183
184 //=======================================================================
185 //function : MakeIdentical
186 //purpose  : Make the shape look the same on the opposite sides in the
187 //           required periodic directions.
188 //=======================================================================
189 void BOPAlgo_MakePeriodic::MakeIdentical()
190 {
191   if (myShape.IsNull())
192     myShape = myInputShape;
193
194   if (mySplitHistory.IsNull())
195     mySplitHistory = new BRepTools_History;
196
197   // Split the negative side of the shape with the geometry
198   // located on the positive side
199   SplitNegative();
200   if (HasErrors())
201     return;
202
203   // Split the positive side of the shape with the geometry
204   // located on the negative side.
205   // Make sure that the opposite sides have identical geometries.
206   // Make associations between identical opposite shapes.
207   SplitPositive();
208
209   myHistory = new BRepTools_History();
210   myHistory->Merge(mySplitHistory);
211 }
212
213 //=======================================================================
214 //function : SplitNegative
215 //purpose  : Split the negative side of the shape with the geometry
216 //           located on the positive side.
217 //=======================================================================
218 void BOPAlgo_MakePeriodic::SplitNegative()
219 {
220   // Copy geometry from positive side of the shape to the negative first.
221   // So, translate the shape in negative periodic directions only.
222   //
223   // To avoid conflicts when copying geometries from positive periodic sides
224   // perform split of each periodic side in a separate operation.
225   for (Standard_Integer i = 0; i < 3; ++i)
226   {
227     if (!IsPeriodic(i))
228       continue;
229
230     // Translate the shape to the negative side
231     gp_Trsf aNegTrsf;
232     aNegTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i].Reversed());
233     BRepBuilderAPI_Transform aNegT(myShape, aNegTrsf, Standard_False);
234
235     // Split the negative side of the shape.
236     TopTools_ListOfShape aTools;
237     aTools.Append(aNegT.Shape());
238     SplitShape(aTools, mySplitHistory);
239   }
240 }
241
242 //=======================================================================
243 //function : AddTwin
244 //purpose  : Associates the shape <theS> with the shape <theTwin> in the map.
245 //=======================================================================
246 static void AddTwin(const TopoDS_Shape& theS,
247                     const TopoDS_Shape& theTwin,
248                     TopTools_DataMapOfShapeListOfShape& theMap)
249 {
250   TopTools_ListOfShape *aTwins = theMap.ChangeSeek(theS);
251   if (!aTwins)
252   {
253     theMap.Bound(theS, TopTools_ListOfShape())->Append(theTwin);
254     return;
255   }
256
257   // Check if the twin shape is not yet present in the list
258   TopTools_ListIteratorOfListOfShape itLT(*aTwins);
259   for (; itLT.More(); itLT.Next())
260   {
261     if (theTwin.IsSame(itLT.Value()))
262       break;
263   }
264
265   if (!itLT.More())
266     aTwins->Append(theTwin);
267 }
268
269 //=======================================================================
270 //function : SplitPositive
271 //purpose  : Split the positive side of the shape with the geometry of the
272 //           negative side. Associate the identical opposite sub-shapes.
273 //=======================================================================
274 void BOPAlgo_MakePeriodic::SplitPositive()
275 {
276   // Prepare map of the sub-shapes of the input shape to make
277   // associations of the opposite shapes
278   TopTools_IndexedMapOfShape aSubShapesMap;
279   TopExp::MapShapes(myShape, aSubShapesMap);
280   const Standard_Integer aNbS = aSubShapesMap.Extent();
281
282   // Translate the shape to the positive periodic directions to make the
283   // shapes look identical on the opposite sides.
284   TopTools_ListOfShape aTools;
285
286   // Remember the history of shapes translation
287   TopTools_IndexedDataMapOfShapeListOfShape aTranslationHistMap;
288
289   // Make translations for all periodic directions
290   for (Standard_Integer i = 0; i < 3; ++i)
291   {
292     if (!IsPeriodic(i))
293       continue;
294
295     // Translate the shape to the positive side
296     gp_Trsf aPosTrsf;
297     aPosTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i]);
298     BRepBuilderAPI_Transform aTranslator(myShape, aPosTrsf, Standard_False);
299     aTools.Append(aTranslator.Shape());
300
301     // Fill the translation history map
302     for (Standard_Integer j = 1; j <= aNbS; ++j)
303     {
304       const TopoDS_Shape& aS = aSubShapesMap(j);
305       if (BRepTools_History::IsSupportedType(aS))
306       {
307         const TopTools_ListOfShape& aSM = aTranslator.Modified(aS);
308         TopTools_ListOfShape* pTS = aTranslationHistMap.ChangeSeek(aS);
309         if (!pTS)
310           pTS = &aTranslationHistMap(aTranslationHistMap.Add(aS, TopTools_ListOfShape()));
311         pTS->Append(aSM.First());
312       }
313     }
314   }
315
316   // Keep the split shape history and history of tools modifications
317   // during the split for making association of the opposite identical shapes
318   Handle(BRepTools_History) aSplitShapeHist = new BRepTools_History,
319                             aSplitToolsHist = new BRepTools_History;
320   // Split the positive side of the shape
321   SplitShape(aTools, aSplitShapeHist, aSplitToolsHist);
322   if (HasErrors())
323     return;
324
325   mySplitHistory->Merge(aSplitShapeHist);
326
327   // Make associations between identical opposite sub-shapes
328   const Standard_Integer aNbSH = aTranslationHistMap.Extent();
329   for (Standard_Integer i = 1; i <= aNbSH; ++i)
330   {
331     const TopoDS_Shape* pS = &aTranslationHistMap.FindKey(i);
332     const TopTools_ListOfShape& aSIm = aSplitShapeHist->Modified(*pS);
333     if (aSIm.Extent() == 1)
334       pS = &aSIm.First();
335     else if (aSIm.Extent() > 1)
336       continue;
337
338     const TopTools_ListOfShape& aLTranslated = aTranslationHistMap(i);
339
340     TopTools_ListIteratorOfListOfShape itLT(aLTranslated);
341     for (; itLT.More(); itLT.Next())
342     {
343       const TopoDS_Shape& aT = itLT.Value();
344       // Get shapes modifications during the split
345       const TopTools_ListOfShape& aTSplits = aSplitToolsHist->Modified(aT);
346
347       // Associate the shapes to each other
348       TopTools_ListIteratorOfListOfShape itSp(aTSplits);
349       for (; itSp.More(); itSp.Next())
350       {
351         const TopoDS_Shape& aSp = itSp.Value();
352         AddTwin(*pS, aSp, myTwins);
353         AddTwin(aSp, *pS, myTwins);
354       }
355     }
356   }
357 }
358
359 //=======================================================================
360 //function : SplitShape
361 //purpose  : Splits the shape by the given tools
362 //=======================================================================
363 void BOPAlgo_MakePeriodic::SplitShape(const TopTools_ListOfShape& theTools,
364                                       Handle(BRepTools_History) theSplitShapeHistory,
365                                       Handle(BRepTools_History) theSplitToolsHistory)
366 {
367   // Make sure that the geometry from the tools will be copied to the split
368   // shape. For that, the tool shapes should be given to the Boolean Operations
369   // algorithm before the shape itself. This will make all coinciding parts
370   // use the geometry of the first argument.
371
372   // Intersection tool for passing ordered arguments
373   BOPAlgo_PaveFiller anIntersector;
374   anIntersector.SetArguments(theTools);
375   // Add the shape
376   anIntersector.AddArgument(myShape);
377   // Use gluing to speed-up intersections
378   anIntersector.SetGlue(BOPAlgo_GlueShift);
379   // Use safe input mode, to avoid reusing geometry of the shape
380   anIntersector.SetNonDestructive(Standard_True);
381   // Set parallel processing mode
382   anIntersector.SetRunParallel(myRunParallel);
383   // Perform Intersection of the arguments
384   anIntersector.Perform();
385   // Check for the errors
386   if (anIntersector.HasErrors())
387   {
388     // Unable to split the shape on opposite sides
389     // Copy the intersection errors
390     myReport->Merge(anIntersector.GetReport());
391     // Add new error saving the shapes for analysis
392     TopoDS_Compound aWS;
393     AddToShape(theTools, aWS);
394     AddToShape(myShape, aWS);
395     AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
396     return;
397   }
398
399   // Perform the splitting of the shape with the precomputed intersection results
400   BRepAlgoAPI_Splitter aSplitter(anIntersector);
401   // Set Object
402   TopTools_ListOfShape anObj;
403   anObj.Append(myShape);
404   aSplitter.SetArguments(anObj);
405   // Set Tools
406   aSplitter.SetTools(theTools);
407   // Use Gluing
408   aSplitter.SetGlue(BOPAlgo_GlueShift);
409   // Set parallel processing mode
410   aSplitter.SetRunParallel(myRunParallel);
411   // Perform splitting
412   aSplitter.Build();
413   // Check for the errors
414   if (aSplitter.HasErrors())
415   {
416     // Unable to split the shape on opposite sides
417     // Copy the splitter errors
418     myReport->Merge(aSplitter.GetReport());
419     // Add new error saving the shape for analysis
420     TopoDS_Compound aWS;
421     AddToShape(theTools, aWS);
422     AddToShape(myShape, aWS);
423     AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
424     return;
425   }
426   // Get the split shape
427   myShape = aSplitter.Shape();
428   // Remember the split history
429   if (!theSplitShapeHistory.IsNull())
430     theSplitShapeHistory->Merge(anObj, aSplitter);
431   if (!theSplitToolsHistory.IsNull())
432     theSplitToolsHistory->Merge(theTools, aSplitter);
433 }
434
435 //=======================================================================
436 //function : RepeatShape
437 //purpose  : Repeats the shape in the required periodic direction
438 //=======================================================================
439 const TopoDS_Shape& BOPAlgo_MakePeriodic::RepeatShape(const Standard_Integer theDir,
440                                                       const Standard_Integer theTimes)
441 {
442   if (myRepeatedShape.IsNull())
443     myRepeatedShape = myShape;
444
445   if (!IsPeriodic(theDir))
446     return myRepeatedShape;
447
448   if (theTimes == 0)
449     return myRepeatedShape;
450
451   // Get the shape's period in the required direction
452   const Standard_Integer id = BOPAlgo_MakePeriodic::ToDirectionID(theDir);
453   if (myRepeatPeriod[id] < Precision::Confusion())
454     myRepeatPeriod[id] = Period(id);
455   const Standard_Real aPeriod = myRepeatPeriod[id];
456
457   // Coefficient to define in which direction the repetition will be performed:
458   // theTimes is positive - in positive direction;
459   // theTimes is negative - in negative direction.
460   const Standard_Integer iDir = theTimes > 0 ? 1 : -1;
461
462   // Create the translation history - all translated shapes will be
463   // created as Generated from the shape.
464   BRepTools_History aTranslationHistory;
465   TopTools_IndexedMapOfShape aSubShapesMap;
466   TopExp::MapShapes(myRepeatedShape, aSubShapesMap);
467   const Standard_Integer aNbS = aSubShapesMap.Extent();
468
469   // Add shapes for gluing
470   TopTools_ListOfShape aShapes;
471   // Add the shape itself
472   aShapes.Append(myRepeatedShape);
473   for (Standard_Integer i = 1; i <= aNbS; ++i)
474   {
475     const TopoDS_Shape& aS = aSubShapesMap(i);
476     if (BRepTools_History::IsSupportedType(aS))
477       aTranslationHistory.AddGenerated(aS, aS);
478   }
479
480   // Create translated copies of the shape
481   for (Standard_Integer i = 1; i <= Abs(theTimes); ++i)
482   {
483     gp_Trsf aTrsf;
484     aTrsf.SetTranslationPart(iDir * i * aPeriod * MY_DIRECTIONS[id]);
485     BRepBuilderAPI_Transform aTranslator(myRepeatedShape, aTrsf, Standard_False);
486     aShapes.Append(aTranslator.Shape());
487
488     // Fill the translation history
489     for (Standard_Integer j = 1; j <= aNbS; ++j)
490     {
491       const TopoDS_Shape& aS = aSubShapesMap(j);
492       if (BRepTools_History::IsSupportedType(aS))
493       {
494         const TopTools_ListOfShape& aLT = aTranslator.Modified(aS);
495         aTranslationHistory.AddGenerated(aS, aLT.First());
496       }
497     }
498   }
499
500   // Update the history with the translation History
501   myHistory->Merge(aTranslationHistory);
502
503   // Glue the translated shapes all together
504   BOPAlgo_Builder aGluer;
505   aGluer.SetArguments(aShapes);
506   // Avoid intersections of the sub-shapes
507   aGluer.SetGlue(BOPAlgo_GlueFull);
508   // Set parallel processing mode
509   aGluer.SetRunParallel(myRunParallel);
510   // Perform gluing
511   aGluer.Perform();
512   if (aGluer.HasErrors())
513   {
514     // Repetition in this direction is not possible
515     // Add warning saving the shapes for analysis
516     TopoDS_Compound aWS;
517     AddToShape(aShapes, aWS);
518     AddWarning(new BOPAlgo_AlertUnableToRepeat(aWS));
519     return myRepeatedShape;
520   }
521   // Get glued shape
522   myRepeatedShape = aGluer.Shape();
523
524   // Update repetition period for the next repetitions
525   myRepeatPeriod[id] += Abs(theTimes) * myRepeatPeriod[id];
526
527   // Update history with the Gluing history
528   BRepTools_History aGluingHistory(aShapes, aGluer);
529   myHistory->Merge(aGluingHistory);
530
531   // Update the map of twins after repetition
532   UpdateTwins(aTranslationHistory, aGluingHistory);
533
534   return myRepeatedShape;
535 }
536
537 //=======================================================================
538 //function : UpdateTwins
539 //purpose  : Updates the map of twins after repetition
540 //=======================================================================
541 void BOPAlgo_MakePeriodic::UpdateTwins(const BRepTools_History& theTranslationHistory,
542                                        const BRepTools_History& theGluingHistory)
543 {
544   if (myTwins.IsEmpty())
545     return;
546
547   if (myRepeatedTwins.IsEmpty())
548     myRepeatedTwins = myTwins;
549
550   // New twins
551   TopTools_DataMapOfShapeListOfShape aNewTwinsMap;
552
553   // Fence map to avoid repeated fill for the twins
554   TopTools_MapOfShape aMTwinsDone;
555
556   // Update the map of twins with the new repeated shapes
557   TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itDMap(myRepeatedTwins);
558   for (; itDMap.More(); itDMap.Next())
559   {
560     const TopoDS_Shape& aS = itDMap.Key();
561     aMTwinsDone.Add(aS);
562
563     const TopTools_ListOfShape& aLTwins = itDMap.Value();
564
565     // Check if the twins have not been already processed
566     TopTools_ListIteratorOfListOfShape itLT(aLTwins);
567     for (; itLT.More(); itLT.Next())
568     {
569       if (aMTwinsDone.Contains(itLT.Value()))
570         break;
571     }
572     if (itLT.More())
573       // Group of twins has already been processed
574       continue;
575
576     // All shapes generated from the shape itself and generated
577     // from its twins will be the new twins for the shape
578     TopTools_IndexedMapOfShape aNewGroup;
579     itLT.Initialize(aLTwins);
580
581     for (Standard_Boolean bShape = Standard_True; itLT.More();)
582     {
583       const TopoDS_Shape& aTwin = bShape ? aS : itLT.Value();
584       const TopTools_ListOfShape& aLG = theTranslationHistory.Generated(aTwin);
585       TopTools_ListIteratorOfListOfShape itLG(aLG);
586       for (; itLG.More(); itLG.Next())
587       {
588         const TopoDS_Shape& aG = itLG.Value();
589         const TopTools_ListOfShape& aLM = theGluingHistory.Modified(aG);
590         if (aLM.IsEmpty())
591           aNewGroup.Add(aG);
592         else
593         {
594           TopTools_ListIteratorOfListOfShape itLM(aLM);
595           for (; itLM.More(); itLM.Next())
596             aNewGroup.Add(itLM.Value());
597         }
598       }
599
600       if (bShape)
601         bShape = Standard_False;
602       else
603         itLT.Next();
604     }
605
606     // Associate the twins to each other
607     const Standard_Integer aNbTwins = aNewGroup.Extent();
608     for (Standard_Integer i = 1; i <= aNbTwins; ++i)
609     {
610       TopTools_ListOfShape* pTwins = aNewTwinsMap.Bound(aNewGroup(i), TopTools_ListOfShape());
611       for (Standard_Integer j = 1; j <= aNbTwins; ++j)
612         if (i != j) pTwins->Append(aNewGroup(j));
613     }
614   }
615
616   myRepeatedTwins = aNewTwinsMap;
617 }