1 // Created on: 2018-03-16
2 // Created by: Eugeny MALTCHIKOV
3 // Copyright (c) 2018 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <BOPAlgo_MakePeriodic.hxx>
18 #include <BOPAlgo_Alerts.hxx>
20 #include <Bnd_Box.hxx>
22 #include <BOPAlgo_Builder.hxx>
23 #include <BOPAlgo_PaveFiller.hxx>
25 #include <BRepAlgoAPI_Common.hxx>
26 #include <BRepAlgoAPI_Splitter.hxx>
28 #include <BRepBndLib.hxx>
30 #include <BRepBuilderAPI_Transform.hxx>
32 #include <BRepPrimAPI_MakeBox.hxx>
36 #include <Precision.hxx>
39 #include <TopoDS_Compound.hxx>
41 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
43 // Periodic/Trim/Repeat directions
44 static const gp_Dir MY_DIRECTIONS[3] = { gp::DX(),
48 //=======================================================================
50 //purpose : Performs the operation
51 //=======================================================================
52 void BOPAlgo_MakePeriodic::Perform()
54 // Check the validity of input data
59 // Trim the shape to fit to the required period in
60 // required periodic directions
65 // Make the shape identical on the opposite sides in
66 // required periodic directions
72 //=======================================================================
73 //function : CheckData
74 //purpose : Checks the validity of input data
75 //=======================================================================
76 void BOPAlgo_MakePeriodic::CheckData()
78 if ( (!IsXPeriodic() || XPeriod() < Precision::Confusion())
79 && (!IsYPeriodic() || YPeriod() < Precision::Confusion())
80 && (!IsZPeriodic() || ZPeriod() < Precision::Confusion()))
82 // Add error informing the user that no periodicity is required
83 // or no valid period is set.
85 AddError(new BOPAlgo_AlertNoPeriodicityRequired());
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)
97 if (theWhere.IsNull())
98 BRep_Builder().MakeCompound(TopoDS::Compound(theWhere));
99 BRep_Builder().Add(theWhere, theWhat);
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)
108 TopTools_ListIteratorOfListOfShape it(theLWhat);
109 for (; it.More(); it.Next())
110 AddToShape(it.Value(), theWhere);
113 //=======================================================================
115 //purpose : Make the trim of the shape to fit to the periodic bounds.
116 //=======================================================================
117 void BOPAlgo_MakePeriodic::Trim()
119 // Check if trim is required at all
120 if (IsInputXTrimmed() &&
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.
129 BRepBndLib::Add(myInputShape, aBox);
130 // Enlarge box to avoid overlapping with the shape
131 aBox.Enlarge(0.1 * sqrt(aBox.SquareExtent()));
133 // Get Corner points of the bounding box
134 gp_Pnt aPMin = aBox.CornerMin();
135 gp_Pnt aPMax = aBox.CornerMax();
137 // Update corner points according to the requested trim parameters
138 for (Standard_Integer i = 0; i < 3; ++i)
140 if (IsInputTrimmed(i))
143 aPMin.SetCoord(i + 1, PeriodFirst(i));
144 aPMax.SetCoord(i + 1, PeriodFirst(i) + Period(i));
147 // Build Trimming solid using corner points
148 BRepPrimAPI_MakeBox aMBox(aPMin, aPMax);
149 const TopoDS_Shape& aTrimBox = aMBox.Solid();
151 // Perform trimming of the shape by solid
152 BRepAlgoAPI_Common aCommon;
154 TopTools_ListOfShape anObj;
155 anObj.Append(myInputShape);
156 aCommon.SetArguments(anObj);
158 TopTools_ListOfShape aTool;
159 aTool.Append(aTrimBox);
160 aCommon.SetTools(aTool);
161 // Set the parallel processing mode
162 aCommon.SetRunParallel(myRunParallel);
165 if (aCommon.HasErrors())
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
172 AddToShape(myInputShape, aWS);
173 AddToShape(aTrimBox, aWS);
174 AddError(new BOPAlgo_AlertUnableToTrim(aWS));
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);
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()
191 if (myShape.IsNull())
192 myShape = myInputShape;
194 if (mySplitHistory.IsNull())
195 mySplitHistory = new BRepTools_History;
197 // Split the negative side of the shape with the geometry
198 // located on the positive side
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.
209 myHistory = new BRepTools_History();
210 myHistory->Merge(mySplitHistory);
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()
220 // Copy geometry from positive side of the shape to the negative first.
221 // So, translate the shape in negative periodic directions only.
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)
230 // Translate the shape to the negative side
232 aNegTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i].Reversed());
233 BRepBuilderAPI_Transform aNegT(myShape, aNegTrsf, Standard_False);
235 // Split the negative side of the shape.
236 TopTools_ListOfShape aTools;
237 aTools.Append(aNegT.Shape());
238 SplitShape(aTools, mySplitHistory);
242 //=======================================================================
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)
250 TopTools_ListOfShape *aTwins = theMap.ChangeSeek(theS);
253 theMap.Bound(theS, TopTools_ListOfShape())->Append(theTwin);
257 // Check if the twin shape is not yet present in the list
258 TopTools_ListIteratorOfListOfShape itLT(*aTwins);
259 for (; itLT.More(); itLT.Next())
261 if (theTwin.IsSame(itLT.Value()))
266 aTwins->Append(theTwin);
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()
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();
282 // Translate the shape to the positive periodic directions to make the
283 // shapes look identical on the opposite sides.
284 TopTools_ListOfShape aTools;
286 // Remember the history of shapes translation
287 TopTools_IndexedDataMapOfShapeListOfShape aTranslationHistMap;
289 // Make translations for all periodic directions
290 for (Standard_Integer i = 0; i < 3; ++i)
295 // Translate the shape to the positive side
297 aPosTrsf.SetTranslationPart(Period(i) * MY_DIRECTIONS[i]);
298 BRepBuilderAPI_Transform aTranslator(myShape, aPosTrsf, Standard_False);
299 aTools.Append(aTranslator.Shape());
301 // Fill the translation history map
302 for (Standard_Integer j = 1; j <= aNbS; ++j)
304 const TopoDS_Shape& aS = aSubShapesMap(j);
305 if (BRepTools_History::IsSupportedType(aS))
307 const TopTools_ListOfShape& aSM = aTranslator.Modified(aS);
308 TopTools_ListOfShape* pTS = aTranslationHistMap.ChangeSeek(aS);
310 pTS = &aTranslationHistMap(aTranslationHistMap.Add(aS, TopTools_ListOfShape()));
311 pTS->Append(aSM.First());
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);
325 mySplitHistory->Merge(aSplitShapeHist);
327 // Make associations between identical opposite sub-shapes
328 const Standard_Integer aNbSH = aTranslationHistMap.Extent();
329 for (Standard_Integer i = 1; i <= aNbSH; ++i)
331 const TopoDS_Shape* pS = &aTranslationHistMap.FindKey(i);
332 const TopTools_ListOfShape& aSIm = aSplitShapeHist->Modified(*pS);
333 if (aSIm.Extent() == 1)
335 else if (aSIm.Extent() > 1)
338 const TopTools_ListOfShape& aLTranslated = aTranslationHistMap(i);
340 TopTools_ListIteratorOfListOfShape itLT(aLTranslated);
341 for (; itLT.More(); itLT.Next())
343 const TopoDS_Shape& aT = itLT.Value();
344 // Get shapes modifications during the split
345 const TopTools_ListOfShape& aTSplits = aSplitToolsHist->Modified(aT);
347 // Associate the shapes to each other
348 TopTools_ListIteratorOfListOfShape itSp(aTSplits);
349 for (; itSp.More(); itSp.Next())
351 const TopoDS_Shape& aSp = itSp.Value();
352 AddTwin(*pS, aSp, myTwins);
353 AddTwin(aSp, *pS, myTwins);
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)
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.
372 // Intersection tool for passing ordered arguments
373 BOPAlgo_PaveFiller anIntersector;
374 anIntersector.SetArguments(theTools);
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())
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
393 AddToShape(theTools, aWS);
394 AddToShape(myShape, aWS);
395 AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
399 // Perform the splitting of the shape with the precomputed intersection results
400 BRepAlgoAPI_Splitter aSplitter(anIntersector);
402 TopTools_ListOfShape anObj;
403 anObj.Append(myShape);
404 aSplitter.SetArguments(anObj);
406 aSplitter.SetTools(theTools);
408 aSplitter.SetGlue(BOPAlgo_GlueShift);
409 // Set parallel processing mode
410 aSplitter.SetRunParallel(myRunParallel);
413 // Check for the errors
414 if (aSplitter.HasErrors())
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
421 AddToShape(theTools, aWS);
422 AddToShape(myShape, aWS);
423 AddError(new BOPAlgo_AlertUnableToMakeIdentical(aWS));
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);
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)
442 if (myRepeatedShape.IsNull())
443 myRepeatedShape = myShape;
445 if (!IsPeriodic(theDir))
446 return myRepeatedShape;
449 return myRepeatedShape;
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];
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;
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();
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)
475 const TopoDS_Shape& aS = aSubShapesMap(i);
476 if (BRepTools_History::IsSupportedType(aS))
477 aTranslationHistory.AddGenerated(aS, aS);
480 // Create translated copies of the shape
481 for (Standard_Integer i = 1; i <= Abs(theTimes); ++i)
484 aTrsf.SetTranslationPart(iDir * i * aPeriod * MY_DIRECTIONS[id]);
485 BRepBuilderAPI_Transform aTranslator(myRepeatedShape, aTrsf, Standard_False);
486 aShapes.Append(aTranslator.Shape());
488 // Fill the translation history
489 for (Standard_Integer j = 1; j <= aNbS; ++j)
491 const TopoDS_Shape& aS = aSubShapesMap(j);
492 if (BRepTools_History::IsSupportedType(aS))
494 const TopTools_ListOfShape& aLT = aTranslator.Modified(aS);
495 aTranslationHistory.AddGenerated(aS, aLT.First());
500 // Update the history with the translation History
501 myHistory->Merge(aTranslationHistory);
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);
512 if (aGluer.HasErrors())
514 // Repetition in this direction is not possible
515 // Add warning saving the shapes for analysis
517 AddToShape(aShapes, aWS);
518 AddWarning(new BOPAlgo_AlertUnableToRepeat(aWS));
519 return myRepeatedShape;
522 myRepeatedShape = aGluer.Shape();
524 // Update repetition period for the next repetitions
525 myRepeatPeriod[id] += Abs(theTimes) * myRepeatPeriod[id];
527 // Update history with the Gluing history
528 BRepTools_History aGluingHistory(aShapes, aGluer);
529 myHistory->Merge(aGluingHistory);
531 // Update the map of twins after repetition
532 UpdateTwins(aTranslationHistory, aGluingHistory);
534 return myRepeatedShape;
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)
544 if (myTwins.IsEmpty())
547 if (myRepeatedTwins.IsEmpty())
548 myRepeatedTwins = myTwins;
551 TopTools_DataMapOfShapeListOfShape aNewTwinsMap;
553 // Fence map to avoid repeated fill for the twins
554 TopTools_MapOfShape aMTwinsDone;
556 // Update the map of twins with the new repeated shapes
557 TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itDMap(myRepeatedTwins);
558 for (; itDMap.More(); itDMap.Next())
560 const TopoDS_Shape& aS = itDMap.Key();
563 const TopTools_ListOfShape& aLTwins = itDMap.Value();
565 // Check if the twins have not been already processed
566 TopTools_ListIteratorOfListOfShape itLT(aLTwins);
567 for (; itLT.More(); itLT.Next())
569 if (aMTwinsDone.Contains(itLT.Value()))
573 // Group of twins has already been processed
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);
581 for (Standard_Boolean bShape = Standard_True; itLT.More();)
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())
588 const TopoDS_Shape& aG = itLG.Value();
589 const TopTools_ListOfShape& aLM = theGluingHistory.Modified(aG);
594 TopTools_ListIteratorOfListOfShape itLM(aLM);
595 for (; itLM.More(); itLM.Next())
596 aNewGroup.Add(itLM.Value());
601 bShape = Standard_False;
606 // Associate the twins to each other
607 const Standard_Integer aNbTwins = aNewGroup.Extent();
608 for (Standard_Integer i = 1; i <= aNbTwins; ++i)
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));
616 myRepeatedTwins = aNewTwinsMap;