0031642: Visualization - crash in Graphic3d_Structure::SetVisual() on redisplaying...
[occt.git] / src / BOPAlgo / BOPAlgo_MakePeriodic.cxx
CommitLineData
53a73fc1 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
44static 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//=======================================================================
52void 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//=======================================================================
76void 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//=======================================================================
94static 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//=======================================================================
105static 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//=======================================================================
117void 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//=======================================================================
189void 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//=======================================================================
218void 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//=======================================================================
246static 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//=======================================================================
274void 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//=======================================================================
363void 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//=======================================================================
439const 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//=======================================================================
541void 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}