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 //=======================================================================
Perform()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 //=======================================================================
CheckData()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 //=======================================================================
AddToShape(const TopoDS_Shape & theWhat,TopoDS_Shape & theWhere)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 //=======================================================================
AddToShape(const TopTools_ListOfShape & theLWhat,TopoDS_Shape & theWhere)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 //=======================================================================
Trim()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 //=======================================================================
MakeIdentical()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 //=======================================================================
SplitNegative()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 //=======================================================================
AddTwin(const TopoDS_Shape & theS,const TopoDS_Shape & theTwin,TopTools_DataMapOfShapeListOfShape & theMap)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 //=======================================================================
SplitPositive()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 //=======================================================================
SplitShape(const TopTools_ListOfShape & theTools,Handle (BRepTools_History)theSplitShapeHistory,Handle (BRepTools_History)theSplitToolsHistory)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 //=======================================================================
RepeatShape(const Standard_Integer theDir,const Standard_Integer theTimes)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 //=======================================================================
UpdateTwins(const BRepTools_History & theTranslationHistory,const BRepTools_History & theGluingHistory)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 }
618