0028284: Avoid classification of sub-shapes of arguments of BOPs relatively solids...
[occt.git] / src / BOPAlgo / BOPAlgo_BOP.cxx
1 // Created by: Peter KURNEV
2 // Copyright (c) 1999-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15
16 #include <BOPAlgo_BOP.hxx>
17 #include <BOPAlgo_BuilderSolid.hxx>
18 #include <BOPAlgo_PaveFiller.hxx>
19 #include <BOPCol_DataMapOfShapeShape.hxx>
20 #include <BOPCol_IndexedDataMapOfShapeListOfShape.hxx>
21 #include <BOPCol_IndexedMapOfShape.hxx>
22 #include <BOPCol_ListOfShape.hxx>
23 #include <BOPCol_MapOfShape.hxx>
24 #include <BOPDS_DS.hxx>
25 #include <BOPTools.hxx>
26 #include <BOPTools_AlgoTools.hxx>
27 #include <BOPTools_AlgoTools3D.hxx>
28 #include <BOPTools_Set.hxx>
29 #include <BOPTools_SetMapHasher.hxx>
30 #include <BRep_Builder.hxx>
31 #include <BRep_Tool.hxx>
32 #include <NCollection_DataMap.hxx>
33 #include <TopAbs_ShapeEnum.hxx>
34 #include <TopExp_Explorer.hxx>
35 #include <TopoDS_Compound.hxx>
36 #include <TopoDS_Edge.hxx>
37 #include <TopoDS_Iterator.hxx>
38 #include <TopoDS_Shape.hxx>
39
40 typedef NCollection_IndexedDataMap
41   <BOPTools_Set,
42   TopoDS_Shape,
43   BOPTools_SetMapHasher> BOPTools_IndexedDataMapOfSetShape;
44 //
45 static
46   TopAbs_ShapeEnum TypeToExplore(const Standard_Integer theDim);
47 //
48 static
49   void CollectContainers(const TopoDS_Shape& theS,
50                          BOPCol_ListOfShape& theLSC);
51 //
52 static
53   void RemoveDuplicates(BOPCol_ListOfShape& theContainers);
54 //
55 static
56   void RemoveDuplicates(BOPCol_ListOfShape& theContainers,
57                         const TopAbs_ShapeEnum theType);
58 //
59 static
60   Standard_Integer NbCommonItemsInMap(const BOPCol_MapOfShape& theM1,
61                                       const BOPCol_MapOfShape& theM2);
62 //
63 static
64   void MapFacesToBuildSolids(const TopoDS_Shape& theSol,
65                              BOPCol_IndexedDataMapOfShapeListOfShape& theMFS,
66                              BOPCol_IndexedMapOfShape& theMFI);
67
68 //=======================================================================
69 //function : 
70 //purpose  : 
71 //=======================================================================
72 BOPAlgo_BOP::BOPAlgo_BOP()
73 :
74   BOPAlgo_Builder(),
75   myTools(myAllocator),
76   myMapTools(100, myAllocator)
77 {
78   Clear();
79 }
80 //=======================================================================
81 //function : 
82 //purpose  : 
83 //=======================================================================
84 BOPAlgo_BOP::BOPAlgo_BOP
85   (const Handle(NCollection_BaseAllocator)& theAllocator)
86 :
87   BOPAlgo_Builder(theAllocator),
88   myTools(myAllocator),
89   myMapTools(100, myAllocator)
90 {
91   Clear();
92 }
93 //=======================================================================
94 //function : ~
95 //purpose  : 
96 //=======================================================================
97 BOPAlgo_BOP::~BOPAlgo_BOP()
98 {
99 }
100 //=======================================================================
101 //function : Clear
102 //purpose  : 
103 //=======================================================================
104 void BOPAlgo_BOP::Clear()
105 {
106   myOperation=BOPAlgo_UNKNOWN;
107   myTools.Clear();
108   myMapTools.Clear();
109   myDims[0]=-1;
110   myDims[1]=-1;
111   //
112   BOPAlgo_Builder::Clear();
113 }
114 //=======================================================================
115 //function : SetOperation
116 //purpose  : 
117 //=======================================================================
118 void BOPAlgo_BOP::SetOperation(const BOPAlgo_Operation theOperation)
119 {
120   myOperation=theOperation;
121 }
122 //=======================================================================
123 //function : Operation
124 //purpose  : 
125 //=======================================================================
126 BOPAlgo_Operation BOPAlgo_BOP::Operation()const
127 {
128   return myOperation;
129 }
130 //=======================================================================
131 //function : AddTool
132 //purpose  : 
133 //=======================================================================
134 void BOPAlgo_BOP::AddTool(const TopoDS_Shape& theShape)
135 {
136   if (myMapTools.Add(theShape)) {
137     myTools.Append(theShape);
138   }
139 }
140 //=======================================================================
141 //function : SetTools
142 //purpose  : 
143 //=======================================================================
144 void BOPAlgo_BOP::SetTools(const BOPCol_ListOfShape& theShapes)
145 {
146   BOPCol_ListIteratorOfListOfShape aIt;
147   //
148   myTools.Clear();
149   aIt.Initialize(theShapes);
150   for (; aIt.More(); aIt.Next()) {
151     const TopoDS_Shape& aS = aIt.Value();
152     AddTool(aS);
153   }
154 }
155 //=======================================================================
156 //function : CheckData
157 //purpose  : 
158 //=======================================================================
159 void BOPAlgo_BOP::CheckData()
160 {
161   Standard_Integer i, j, iDim, aNbArgs, aNbTools;
162   Standard_Boolean bFlag, bFuse;
163   BOPCol_ListIteratorOfListOfShape aItLS;
164   //
165   myErrorStatus=0;
166   //
167   if (!(myOperation==BOPAlgo_COMMON ||
168         myOperation==BOPAlgo_FUSE || 
169         myOperation==BOPAlgo_CUT|| 
170         myOperation==BOPAlgo_CUT21)) {
171     // non-licit operation
172     myErrorStatus=14;
173     return;
174   }
175   //
176   aNbArgs=myArguments.Extent();
177   if (!aNbArgs) {
178     // invalid number of Arguments
179     myErrorStatus=100; 
180     return;
181   }
182   //
183   aNbTools=myTools.Extent();
184   if (!aNbTools) { 
185     // invalid number of Tools
186     myErrorStatus=100;
187     return;
188   }
189   //
190   if (!myPaveFiller) {
191     myErrorStatus=101; 
192     return;
193   }
194   //
195   myErrorStatus=myPaveFiller->ErrorStatus();
196   if (myErrorStatus) {
197     return;
198   }
199   //
200   bFuse = (myOperation == BOPAlgo_FUSE);
201   //
202   // The rules for different types of operations are the following:
203   // 1. FUSE:   All arguments and tools should have the same dimension;
204   // 2. CUT:    The MAXIMAL dimension of the ARGUMENTS should be less
205   //            or equal to the MINIMAL dimension of the TOOLS;
206   // 3. CUT21:  The MINIMAL dimension of ARGUMENTS should be grater
207   //            or equal to the MAXIMAL dimension of the TOOLS;
208   // 4. COMMON: The arguments and tools could have any dimensions.
209   //
210   Standard_Integer iDimMin[2], iDimMax[2];
211   //
212   for (i=0; i<2; ++i) {
213     const BOPCol_ListOfShape& aLS=(!i)? myArguments : myTools;
214     aItLS.Initialize(aLS);
215     for (j=0; aItLS.More(); aItLS.Next(), ++j) {
216       const TopoDS_Shape& aS=aItLS.Value();
217       bFlag=BOPTools_AlgoTools3D::IsEmptyShape(aS);
218       if(bFlag) {
219         myWarningStatus=2;
220       }
221       //
222       iDim=BOPTools_AlgoTools::Dimension(aS);
223       if (iDim<0) {
224         // non-homogenious argument
225         myErrorStatus=13; 
226         return;
227       }
228       //
229       if (!j) {
230         iDimMin[i] = iDim;
231         iDimMax[i] = iDim;
232         continue;
233       }
234       //
235       if (iDim < iDimMin[i]) {
236         iDimMin[i] = iDim;
237       }
238       else if (iDim > iDimMax[i]) {
239         iDimMax[i] = iDim;
240       }
241       //
242       if (bFuse && (iDimMin[i] != iDimMax[i])) {
243         // non-homogenious argument
244         myErrorStatus=13;
245         return;
246       }
247     }
248   }
249   //
250   if (((myOperation == BOPAlgo_FUSE)  && (iDimMax[0] != iDimMax[1])) ||
251       ((myOperation == BOPAlgo_CUT)   && (iDimMax[0] >  iDimMin[1])) ||
252       ((myOperation == BOPAlgo_CUT21) && (iDimMin[0] <  iDimMax[1])) ) {
253     // non-licit operation for the arguments
254     myErrorStatus=14;
255     return;
256   }
257   //
258   myDims[0] = iDimMin[0];
259   myDims[1] = iDimMin[1];
260 }
261 //=======================================================================
262 //function : Prepare
263 //purpose  : 
264 //=======================================================================
265 void BOPAlgo_BOP::Prepare()
266 {
267   //
268   BOPAlgo_Builder::Prepare();
269   //
270   if(myWarningStatus == 2) {
271     Standard_Integer i;
272     BRep_Builder aBB;
273     BOPCol_ListIteratorOfListOfShape aItLS;
274     //
275     switch(myOperation) {
276       case BOPAlgo_FUSE: {
277         for (i=0; i<2; ++i) {
278           const BOPCol_ListOfShape& aLS=(!i)? myArguments : myTools;
279           aItLS.Initialize(aLS);
280           for (; aItLS.More(); aItLS.Next()) {
281             const TopoDS_Shape& aS=aItLS.Value();
282             aBB.Add(myShape, aS);
283           }
284         }
285       }
286         break;
287       //  
288       case BOPAlgo_CUT: {
289         aItLS.Initialize(myArguments);
290         for (; aItLS.More(); aItLS.Next()) {
291           const TopoDS_Shape& aS=aItLS.Value();
292           if(!BOPTools_AlgoTools3D::IsEmptyShape(aS)) {
293             aBB.Add(myShape, aS);
294           } 
295         }
296       }
297         break;
298       
299       case BOPAlgo_CUT21: {
300         aItLS.Initialize(myTools);
301         for (; aItLS.More(); aItLS.Next()) {
302           const TopoDS_Shape& aS=aItLS.Value();
303           if(!BOPTools_AlgoTools3D::IsEmptyShape(aS)) {
304             aBB.Add(myShape, aS);
305           } 
306         }
307       }
308         break;
309         //
310       default:
311         break;
312       }
313   }
314 }
315 //=======================================================================
316 //function : BuildResult
317 //purpose  : 
318 //=======================================================================
319 void BOPAlgo_BOP::BuildResult(const TopAbs_ShapeEnum theType)
320 {
321   TopAbs_ShapeEnum aType;
322   BRep_Builder aBB;
323   BOPCol_MapOfShape aM;
324   BOPCol_ListIteratorOfListOfShape aIt, aItIm;
325   //
326   myErrorStatus=0;
327   //
328   const BOPCol_ListOfShape& aLA=myDS->Arguments();
329   aIt.Initialize(aLA);
330   for (; aIt.More(); aIt.Next()) {
331     const TopoDS_Shape& aS=aIt.Value();
332     aType=aS.ShapeType();
333     if (aType==theType) {
334       if (myImages.IsBound(aS)){
335         const BOPCol_ListOfShape& aLSIm=myImages.Find(aS);
336         aItIm.Initialize(aLSIm);
337         for (; aItIm.More(); aItIm.Next()) {
338           const TopoDS_Shape& aSIm=aItIm.Value();
339           if (aM.Add(aSIm)) {
340             aBB.Add(myShape, aSIm);
341           }
342         }
343       }
344       else {
345         if (aM.Add(aS)) {
346           aBB.Add(myShape, aS);
347         }
348       }
349     }
350   }
351 }
352 //=======================================================================
353 //function : Perform
354 //purpose  : 
355 //=======================================================================
356 void BOPAlgo_BOP::Perform()
357 {
358   Handle(NCollection_BaseAllocator) aAllocator;
359   BOPAlgo_PaveFiller* pPF;
360   BOPCol_ListIteratorOfListOfShape aItLS;
361   //
362   myErrorStatus=0;
363   //
364   if (myEntryPoint==1) {
365     if (myPaveFiller) {
366       delete myPaveFiller;
367       myPaveFiller=NULL;
368     }
369   }
370   //
371   aAllocator=
372     NCollection_BaseAllocator::CommonBaseAllocator();
373   BOPCol_ListOfShape aLS(aAllocator);
374   //
375   aItLS.Initialize(myArguments);
376   for (; aItLS.More(); aItLS.Next()) {
377     const TopoDS_Shape& aS=aItLS.Value();
378     aLS.Append(aS);
379   }
380   //
381   aItLS.Initialize(myTools);
382   for (; aItLS.More(); aItLS.Next()) {
383     const TopoDS_Shape& aS=aItLS.Value();
384     aLS.Append(aS);
385   }
386   //
387   pPF=new BOPAlgo_PaveFiller(aAllocator);
388   pPF->SetArguments(aLS);
389   pPF->SetRunParallel(myRunParallel);
390   pPF->SetProgressIndicator(myProgressIndicator);
391   pPF->SetFuzzyValue(myFuzzyValue);
392   pPF->SetNonDestructive(myNonDestructive);
393   pPF->SetGlue(myGlue);
394   //
395   pPF->Perform();
396   //
397   myEntryPoint=1;
398   PerformInternal(*pPF);
399 }
400 //=======================================================================
401 //function : PerformInternal1
402 //purpose  : 
403 //=======================================================================
404 void BOPAlgo_BOP::PerformInternal1(const BOPAlgo_PaveFiller& theFiller)
405 {
406   myErrorStatus=0;
407   myWarningStatus=0;
408   //
409   myPaveFiller=(BOPAlgo_PaveFiller*)&theFiller;
410   myDS=myPaveFiller->PDS();
411   myContext=myPaveFiller->Context();
412   myFuzzyValue = myPaveFiller->FuzzyValue();
413   myNonDestructive = myPaveFiller->NonDestructive();
414   //
415   // 1. CheckData
416   CheckData();
417   if (myErrorStatus && !myWarningStatus) {
418     return;
419   }
420   //
421   // 2. Prepare
422   Prepare();
423   if (myErrorStatus) {
424     return;
425   }
426   //
427   if(myWarningStatus == 2) {
428     return;
429   }
430   // 3. Fill Images
431   // 3.1 Vertices
432   FillImagesVertices();
433   if (myErrorStatus) {
434     return;
435   }
436   //
437   BuildResult(TopAbs_VERTEX);
438   if (myErrorStatus) {
439     return;
440   }
441   // 3.2 Edges
442   FillImagesEdges();
443   if (myErrorStatus) {
444     return;
445   }
446   //
447   BuildResult(TopAbs_EDGE);
448   if (myErrorStatus) {
449     return;
450   }
451   //
452   // 3.3 Wires
453   FillImagesContainers(TopAbs_WIRE);
454   if (myErrorStatus) {
455     return;
456   }
457   //
458   BuildResult(TopAbs_WIRE);
459   if (myErrorStatus) {
460     return;
461   }
462   //
463   // 3.4 Faces
464   FillImagesFaces();
465   if (myErrorStatus) {
466     return;
467   }
468   
469   BuildResult(TopAbs_FACE);
470   if (myErrorStatus) {
471     return;
472   }
473   //
474   // 3.5 Shells
475   FillImagesContainers(TopAbs_SHELL);
476   if (myErrorStatus) {
477     return;
478   }
479   //
480   BuildResult(TopAbs_SHELL);
481   if (myErrorStatus) {
482     return;
483   }
484   //
485   // 3.6 Solids
486   FillImagesSolids();
487   if (myErrorStatus) {
488     return;
489   }
490   //
491   BuildResult(TopAbs_SOLID);
492   if (myErrorStatus) {
493     return;
494   }
495   //
496   // 3.7 CompSolids
497   FillImagesContainers(TopAbs_COMPSOLID);
498   if (myErrorStatus) {
499     return;
500   }
501   //
502   BuildResult(TopAbs_COMPSOLID);
503   if (myErrorStatus) {
504     return;
505   }
506   //
507   // 3.8 Compounds
508   FillImagesCompounds();
509   if (myErrorStatus) {
510     return;
511   }
512   //
513   BuildResult(TopAbs_COMPOUND);
514   if (myErrorStatus) {
515     return;
516   }
517   //
518   // 4.BuildShape;
519   BuildShape();
520   if (myErrorStatus) {
521     return;
522   }
523   // 
524   // 5.History
525   PrepareHistory();
526   //
527   // 6 Post-treatment 
528   PostTreat();
529 }
530 //=======================================================================
531 //function : BuildRC
532 //purpose  : 
533 //=======================================================================
534 void BOPAlgo_BOP::BuildRC()
535 {
536   TopAbs_ShapeEnum aType;
537   TopoDS_Compound aC;
538   BRep_Builder aBB;
539   //
540   myErrorStatus = 0;
541   //
542   aBB.MakeCompound(aC);
543   //
544   // A. Fuse
545   if (myOperation == BOPAlgo_FUSE) {
546     BOPCol_MapOfShape aMFence;
547     aType = TypeToExplore(myDims[0]);
548     TopExp_Explorer aExp(myShape, aType);
549     for (; aExp.More(); aExp.Next()) {
550       const TopoDS_Shape& aS = aExp.Current();
551       if (aMFence.Add(aS)) {
552         aBB.Add(aC, aS);
553       }
554     }
555     myRC = aC;
556     return;
557   }
558   //
559   // B. Common, Cut, Cut21
560   //
561   Standard_Integer i, j, aNb, iDim;
562   Standard_Boolean bCheckEdges, bContains, bCut21, bCommon;
563   BOPCol_ListIteratorOfListOfShape aItLS;
564   //
565   // prepare the building elements of arguments to get its splits
566   BOPCol_IndexedMapOfShape aMArgs, aMTools;
567   for (i = 0; i < 2; ++i) {
568     const BOPCol_ListOfShape& aLS = !i ? myArguments : myTools;
569     BOPCol_IndexedMapOfShape& aMS = !i ? aMArgs : aMTools;
570     aItLS.Initialize(aLS);
571     for (; aItLS.More(); aItLS.Next()) {
572       const TopoDS_Shape& aS = aItLS.Value();
573       iDim = BOPTools_AlgoTools::Dimension(aS);
574       aType = TypeToExplore(iDim);
575       BOPTools::MapShapes(aS, aType, aMS);
576     }
577   }
578   //
579   bCheckEdges = Standard_False;
580   //
581   // get splits of building elements
582   BOPCol_IndexedMapOfShape aMArgsIm, aMToolsIm;
583   BOPTools_IndexedDataMapOfSetShape aMSetArgs, aMSetTools;
584
585   for (i = 0; i < 2; ++i) {
586     const BOPCol_IndexedMapOfShape& aMS = !i ? aMArgs : aMTools;
587     BOPCol_IndexedMapOfShape& aMSIm = !i ? aMArgsIm : aMToolsIm;
588     BOPTools_IndexedDataMapOfSetShape& aMSet = !i ? aMSetArgs : aMSetTools;
589     //
590     aNb = aMS.Extent();
591     for (j = 1; j <= aNb; ++j) {
592       const TopoDS_Shape& aS = aMS(j);
593       aType = aS.ShapeType();
594       if (aType == TopAbs_EDGE) {
595         const TopoDS_Edge& aE = *(TopoDS_Edge*)&aS;
596         bCheckEdges = Standard_True;
597         if (BRep_Tool::Degenerated(aE)) {
598           continue;
599         }
600       }
601       //
602       if (myImages.IsBound(aS)) {
603         const BOPCol_ListOfShape& aLSIm = myImages.Find(aS);
604         aItLS.Initialize(aLSIm);
605         for (; aItLS.More(); aItLS.Next()) {
606           const TopoDS_Shape& aSIm = aItLS.Value();
607           aMSIm.Add(aSIm);
608         }
609       }
610       else {
611         aMSIm.Add(aS);
612         if (aS.ShapeType() == TopAbs_SOLID) {
613           BOPTools_Set aST;
614           aST.Add(aS, TopAbs_FACE);
615           if (!aMSet.Contains(aST)) {
616             aMSet.Add(aST, aS);
617           }
618         }
619       }
620     }
621   }
622   //
623   // compare the maps and make the result
624   //
625   Standard_Integer iDimMin, iDimMax;
626   //
627   iDimMin = Min(myDims[0], myDims[1]);
628   bCommon = (myOperation == BOPAlgo_COMMON);
629   bCut21  = (myOperation == BOPAlgo_CUT21);
630   //
631   const BOPCol_IndexedMapOfShape& aMIt = bCut21 ? aMToolsIm : aMArgsIm;
632   const BOPCol_IndexedMapOfShape& aMCheck = bCut21 ? aMArgsIm : aMToolsIm;
633   const BOPTools_IndexedDataMapOfSetShape& aMSetCheck = bCut21 ? aMSetArgs : aMSetTools;
634   //
635   BOPCol_IndexedMapOfShape aMCheckExp, aMItExp;
636   //
637   if (bCommon) {
638     aNb = aMIt.Extent();
639     for (i = 1; i <= aNb; ++i) {
640       const TopoDS_Shape& aS = aMIt(i);
641       iDimMax = BOPTools_AlgoTools::Dimension(aS);
642       for (iDim = iDimMin; iDim < iDimMax; ++iDim) {
643         aType = TypeToExplore(iDim);
644         BOPTools::MapShapes(aS, aType, aMItExp);
645       }
646       aMItExp.Add(aS);
647     }
648   }
649   else {
650     aMItExp = aMIt;
651   }
652   //
653   aNb = aMCheck.Extent();
654   for (i = 1; i <= aNb; ++i) {
655     const TopoDS_Shape& aS = aMCheck(i);
656     iDimMax = BOPTools_AlgoTools::Dimension(aS);
657     for (iDim = iDimMin; iDim < iDimMax; ++iDim) {
658       aType = TypeToExplore(iDim);
659       BOPTools::MapShapes(aS, aType, aMCheckExp);
660     }
661     aMCheckExp.Add(aS);
662   }
663   //
664   aNb = aMItExp.Extent();
665   for (i = 1; i <= aNb; ++i) {
666     const TopoDS_Shape& aS = aMItExp(i);
667     //
668     bContains = aMCheckExp.Contains(aS);
669     if (!bContains && aS.ShapeType() == TopAbs_SOLID) {
670       BOPTools_Set aST;
671       aST.Add(aS, TopAbs_FACE);
672       bContains = aMSetCheck.Contains(aST);
673     }
674     //
675     if (bCommon) {
676       if (bContains) {
677         aBB.Add(aC, aS);
678       }
679     }
680     else {
681       if (!bContains) {
682         aBB.Add(aC, aS);
683       }
684     }
685   }
686   //
687   // filter result for COMMON operation
688   if (bCommon) {
689     BOPCol_MapOfShape aMFence;
690     TopExp_Explorer aExp;
691     TopoDS_Compound aCx;
692     aBB.MakeCompound(aCx);
693     //
694     for (iDim = 3; iDim >= iDimMin; --iDim) {
695       aType = TypeToExplore(iDim);
696       aExp.Init(aC, aType);
697       for (; aExp.More(); aExp.Next()) {
698         const TopoDS_Shape& aS = aExp.Current();
699         if (aMFence.Add(aS)) {
700           aBB.Add(aCx, aS);
701           BOPTools::MapShapes(aS, aMFence);
702         }
703       }
704     }
705     aC = aCx;
706   }
707   //
708   if (!bCheckEdges) {
709     myRC = aC;
710     return;
711   }
712   //
713   // The squats around degenerated edges
714   Standard_Integer nVD;
715   BOPCol_IndexedMapOfShape aMVC;
716   //
717   // 1. Vertices of aC
718   BOPTools::MapShapes(aC, TopAbs_VERTEX, aMVC);
719   //
720   // 2. DE candidates
721   aNb = myDS->NbSourceShapes();
722   for (i = 0; i < aNb; ++i) {
723     const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(i);
724     aType = aSI.ShapeType();
725     if (aType != TopAbs_EDGE) {
726       continue;
727     }
728     //
729     const TopoDS_Edge& aE = *((TopoDS_Edge*)&aSI.Shape());
730     if (!BRep_Tool::Degenerated(aE)) {
731       continue;
732     }
733     //
734     nVD = aSI.SubShapes().First();
735     const TopoDS_Shape& aVD = myDS->Shape(nVD);
736     //
737     if (!aMVC.Contains(aVD)) {
738       continue;
739     }
740     //
741     if (myDS->IsNewShape(nVD)) {
742       continue;
743     }
744     //
745     if (myDS->HasInterf(nVD)) {
746       continue;
747     }
748     //
749     aBB.Add(aC, aE);
750   }
751   //
752   myRC=aC;
753 }
754 //=======================================================================
755 //function : BuildShape
756 //purpose  : 
757 //=======================================================================
758 void BOPAlgo_BOP::BuildShape()
759 {
760   BuildRC();
761   //
762   if ((myOperation == BOPAlgo_FUSE) && (myDims[0] == 3)) {
763     BuildSolid();
764     return;
765   }
766   //
767   Standard_Integer i;
768   TopAbs_ShapeEnum aType, aT1, aT2;
769   BOPCol_ListOfShape aLSC, aLCB;
770   BOPCol_ListIteratorOfListOfShape aItLS, aItLSIm, aItLCB;
771   TopoDS_Iterator aIt;
772   BRep_Builder aBB;
773   TopoDS_Shape aRC, aRCB;
774   //
775   BOPCol_MapOfShape aMSRC;
776   BOPTools::MapShapes(myRC, aMSRC);
777   //
778   // collect images of containers
779   for (i = 0; i < 2; ++i) {
780     const BOPCol_ListOfShape& aLS = !i ? myArguments : myTools;
781     //
782     aItLS.Initialize(aLS);
783     for (; aItLS.More(); aItLS.Next()) {
784       const TopoDS_Shape& aS = aItLS.Value();
785       //
786       CollectContainers(aS, aLSC);
787     }
788   }
789   // make containers
790   BOPCol_ListOfShape aLCRes;
791   aItLS.Initialize(aLSC);
792   for (; aItLS.More(); aItLS.Next()) {
793     const TopoDS_Shape& aSC = aItLS.Value();
794     //
795     BOPTools_AlgoTools::MakeContainer(TopAbs_COMPOUND, aRC);
796     //
797     aIt.Initialize(aSC);
798     for (; aIt.More(); aIt.Next()) {
799       const TopoDS_Shape& aS = aIt.Value();
800       if (myImages.IsBound(aS)) {
801         const BOPCol_ListOfShape& aLSIm = myImages.Find(aS);
802         //
803         aItLSIm.Initialize(aLSIm);
804         for (; aItLSIm.More(); aItLSIm.Next()) {
805           const TopoDS_Shape& aSIm = aItLSIm.Value();
806           if (aMSRC.Contains(aSIm)) {
807             aBB.Add(aRC, aSIm);
808           }
809         }
810       }
811       else if (aMSRC.Contains(aS)) {
812         aBB.Add(aRC, aS);
813       }
814     }
815     //
816     aType = aSC.ShapeType();
817     switch (aType) {
818       case TopAbs_WIRE: {
819         aT1 = TopAbs_VERTEX;
820         aT2 = TopAbs_EDGE;
821         break;
822       }
823       case TopAbs_SHELL: {
824         aT1 = TopAbs_EDGE;
825         aT2 = TopAbs_FACE;
826         break;
827       }
828       default: {
829         aT1 = TopAbs_FACE;
830         aT2 = TopAbs_SOLID;
831       }
832     }
833     //
834     aLCB.Clear();
835     BOPTools_AlgoTools::MakeConnexityBlocks(aRC, aT1, aT2, aLCB);
836     if (aLCB.IsEmpty()) {
837       continue;
838     }
839     //
840     aItLCB.Initialize(aLCB);
841     for (; aItLCB.More(); aItLCB.Next()) {
842       BOPTools_AlgoTools::MakeContainer(aType, aRCB);
843       //
844       const TopoDS_Shape& aCB = aItLCB.Value();
845       aIt.Initialize(aCB);
846       for (; aIt.More(); aIt.Next()) {
847         const TopoDS_Shape& aCBS = aIt.Value();
848         aBB.Add(aRCB, aCBS);
849       }
850       //
851       if (aType == TopAbs_WIRE) {
852         // reorient wire
853         BOPTools_AlgoTools::OrientEdgesOnWire(aRCB);
854       }
855       else if (aType == TopAbs_SHELL) {
856         BOPTools_AlgoTools::OrientFacesOnShell(aRCB);
857       }
858       //
859       aRCB.Orientation(aSC.Orientation());
860       //
861       aLCRes.Append(aRCB);
862     }
863   }
864   //
865   RemoveDuplicates(aLCRes);
866   //
867   // add containers to result
868   TopoDS_Compound aResult;
869   aBB.MakeCompound(aResult);
870   //
871   aItLS.Initialize(aLCRes);
872   for (; aItLS.More(); aItLS.Next()) {
873     aBB.Add(aResult, aItLS.Value());
874   }
875   //
876   // add the rest of the shapes into result
877   BOPCol_MapOfShape aMSResult;
878   BOPTools::MapShapes(aResult, aMSResult);
879   //
880   aIt.Initialize(myRC);
881   for (; aIt.More(); aIt.Next()) {
882     const TopoDS_Shape& aS = aIt.Value();
883     if (aMSResult.Add(aS)) {
884       aBB.Add(aResult, aS);
885     }
886   }
887   //
888   myShape = aResult;
889 }
890 //=======================================================================
891 //function : BuildSolid
892 //purpose  : 
893 //=======================================================================
894 void BOPAlgo_BOP::BuildSolid()
895 {
896   // Containers
897   BOPCol_ListOfShape aLSC;
898   //
899   BOPCol_ListIteratorOfListOfShape aItLS;
900   TopExp_Explorer aExp;
901   BRep_Builder aBB;
902   //
903   myErrorStatus=0;
904   //
905   // Get solids from input arguments
906   BOPCol_MapOfShape aMSA;
907   // Map the arguments to find shared faces
908   BOPCol_IndexedDataMapOfShapeListOfShape aMFS;
909   for (Standard_Integer i = 0; i < 2; ++i) {
910     const BOPCol_ListOfShape& aLSA = (i) ? myArguments : myTools;
911     aItLS.Initialize(aLSA);
912     for (; aItLS.More(); aItLS.Next()) {
913       const TopoDS_Shape& aSA = aItLS.Value();
914       aExp.Init(aSA, TopAbs_SOLID);
915       for (; aExp.More(); aExp.Next()) {
916         const TopoDS_Shape& aSol = aExp.Current();
917         aMSA.Add(aSol);
918         BOPTools::MapShapesAndAncestors(aSol, TopAbs_FACE, TopAbs_SOLID, aMFS);
919       }
920       //
921       // get Compsolids from input arguments
922       CollectContainers(aSA, aLSC);
923     }
924   }
925   //
926   // Find solids in input arguments sharing faces with other solids
927   BOPCol_MapOfShape aMTSols;
928   Standard_Integer i, aNb = aMFS.Extent();
929   for (i = 1; i < aNb; ++i) {
930     const BOPCol_ListOfShape& aLSols = aMFS(i);
931     if (aLSols.Extent() > 1) {
932       aItLS.Initialize(aLSols);
933       for(; aItLS.More(); aItLS.Next()) {
934         aMTSols.Add(aItLS.Value());
935       }
936     }
937   }
938   //
939   // Possibly untouched solids - to be added to results as is
940   BOPCol_IndexedMapOfShape aMUSols;
941   // Use map to chose the most outer faces to build result solids
942   aMFS.Clear();
943   // Internal faces
944   BOPCol_IndexedMapOfShape aMFI;
945   //
946   TopoDS_Iterator aIt(myRC);
947   for (; aIt.More(); aIt.Next()) {
948     const TopoDS_Shape& aSx = aIt.Value();
949     if (aMSA.Contains(aSx)) {
950       if (!aMTSols.Contains(aSx)) {
951         aMUSols.Add(aSx);
952         continue;
953       }
954     }
955     //
956     MapFacesToBuildSolids(aSx, aMFS, aMFI);
957   } // for (; aIt.More(); aIt.Next()) {
958   //
959   // Process possibly untouched solids.
960   // Really untouched solids will be added into result as is.
961   // Others will be processed by BuilderSolid.
962   BOPTools_IndexedDataMapOfSetShape aDMSTS;
963   //
964   aNb = aMUSols.Extent();
965   for (i = 1; i <= aNb; ++i) {
966     const TopoDS_Shape& aSx = aMUSols(i);
967     //
968     aExp.Init(aSx, TopAbs_FACE);
969     for (; aExp.More(); aExp.Next()) {
970       if (aMFS.Contains(aExp.Current())) {
971         break;
972       }
973     }
974     //
975     if (aExp.More()) {
976       MapFacesToBuildSolids(aSx, aMFS, aMFI);
977     }
978     else {
979       BOPTools_Set aST;
980       aST.Add(aSx, TopAbs_FACE);
981       if (!aDMSTS.Contains(aST)) {
982         aDMSTS.Add(aST, aSx);
983       }
984     }
985   }
986   //
987   BOPCol_IndexedDataMapOfShapeListOfShape aMEF;
988   // Split faces will be added in the end
989   // to avoid errors in BuilderSolid algorithm
990   BOPCol_ListOfShape aLF, aLFx;
991   aNb = aMFS.Extent();
992   for (i = 1; i <= aNb; ++i) {
993     const BOPCol_ListOfShape& aLSx = aMFS(i);
994     if (aLSx.Extent() == 1) {
995       const TopoDS_Shape& aFx = aMFS.FindKey(i);
996       BOPTools::MapShapesAndAncestors(aFx, TopAbs_EDGE, TopAbs_FACE, aMEF);
997       if (IsBoundSplits(aFx, aMEF)){
998         aLFx.Append(aFx);
999         continue;
1000       }
1001       aLF.Append(aFx);
1002     }
1003   }
1004   //
1005   // Faces to build result solids
1006   BOPCol_ListOfShape aSFS;
1007   aItLS.Initialize(aLF);
1008   for(; aItLS.More(); aItLS.Next()) {
1009     const TopoDS_Shape& aFx = aItLS.Value();
1010     aSFS.Append(aFx);
1011   }
1012   //
1013   // Split faces
1014   aItLS.Initialize(aLFx);
1015   for (; aItLS.More(); aItLS.Next()) {
1016     const TopoDS_Shape& aFx = aItLS.Value();
1017     aSFS.Append(aFx);
1018   }
1019   //
1020   // Internal faces
1021   aNb = aMFI.Extent();
1022   for (i = 1; i <= aNb; ++i) {
1023     TopoDS_Shape aFx = aMFI.FindKey(i);
1024     aSFS.Append(aFx.Oriented(TopAbs_FORWARD));
1025     aSFS.Append(aFx.Oriented(TopAbs_REVERSED));
1026   }
1027   //
1028   TopoDS_Shape aRC;
1029   BOPTools_AlgoTools::MakeContainer(TopAbs_COMPOUND, aRC);
1030   if (aSFS.Extent()) {
1031     // Build solids from set of faces
1032     BOPAlgo_BuilderSolid aSB;
1033     aSB.SetContext(myContext);
1034     aSB.SetShapes(aSFS);
1035     aSB.Perform();
1036     if (aSB.ErrorStatus()) {
1037       myErrorStatus = 30; // SolidBuilder failed
1038       return;
1039     }
1040     // new solids
1041     const BOPCol_ListOfShape& aLSR = aSB.Areas();
1042     //
1043     // add new solids to result
1044     aItLS.Initialize(aLSR);
1045     for (; aItLS.More(); aItLS.Next()) {
1046       const TopoDS_Shape& aSR = aItLS.Value();
1047       aBB.Add(aRC, aSR);
1048     }
1049   }
1050   //
1051   // add untouched solids to result
1052   aNb = aDMSTS.Extent();
1053   for (i = 1; i <= aNb; ++i) {
1054     const TopoDS_Shape& aSx = aDMSTS(i);
1055     aBB.Add(aRC, aSx);
1056   }
1057   //
1058   if (aLSC.IsEmpty()) {
1059     // no Compsolids in arguments
1060     myShape = aRC;
1061     return;
1062   }
1063   //
1064   // build new Compsolids from new solids containing splits
1065   // of faces from arguments of type Compsolid
1066   //
1067   TopoDS_Shape aResult;
1068   BOPTools_AlgoTools::MakeContainer(TopAbs_COMPOUND, aResult);
1069   //
1070   aIt.Initialize(aRC);
1071   if (!aIt.More()) {
1072     // no solids in the result
1073     myShape = aRC;
1074     return;
1075   }
1076   //
1077   const TopoDS_Shape& aSol1 = aIt.Value();
1078   aIt.Next();
1079   //
1080   // optimization for one solid in the result
1081   if (!aIt.More()) {
1082     TopoDS_Shape aCS;
1083     BOPTools_AlgoTools::MakeContainer(TopAbs_COMPSOLID, aCS);
1084     aBB.Add(aCS, aSol1);
1085     //
1086     aBB.Add(aResult, aCS);
1087     myShape = aResult;
1088     return;
1089   }
1090   //
1091   // get splits of faces of the Compsolid arguments
1092   BOPCol_MapOfShape aMFCs;
1093   aItLS.Initialize(aLSC);
1094   for (; aItLS.More(); aItLS.Next()) {
1095     const TopoDS_Shape& aCs = aItLS.Value();
1096     aExp.Init(aCs, TopAbs_FACE);
1097     for (; aExp.More(); aExp.Next()) {
1098       const TopoDS_Shape& aF = aExp.Current();
1099       const BOPCol_ListOfShape* pLFIm = myImages.Seek(aF);
1100       if (!pLFIm) {
1101         aMFCs.Add(aF);
1102       }
1103       else {
1104         BOPCol_ListIteratorOfListOfShape aItLFIm(*pLFIm);
1105         for (; aItLFIm.More(); aItLFIm.Next()) {
1106           aMFCs.Add(aItLFIm.Value());
1107         }
1108       }
1109     }
1110   }
1111   //
1112   // build connexity blocks from new solids
1113   BOPCol_ListOfShape aLCBS;
1114   BOPTools_AlgoTools::MakeConnexityBlocks(aRC, TopAbs_FACE, TopAbs_SOLID, aLCBS);
1115   //
1116   aItLS.Initialize(aLCBS);
1117   for (; aItLS.More(); aItLS.Next()) {
1118     const TopoDS_Shape& aCB = aItLS.Value();
1119     //
1120     // check if the Compsolid should be created
1121     aExp.Init(aCB, TopAbs_FACE);
1122     for (; aExp.More(); aExp.Next()) {
1123       if (aMFCs.Contains(aExp.Current())) {
1124         break;
1125       }
1126     }
1127     //
1128     if (!aExp.More()) {
1129       // add solids directly into result as their origins are not Compsolids
1130       for (aIt.Initialize(aCB); aIt.More(); aIt.Next()) {
1131         aBB.Add(aResult, aIt.Value());
1132       }
1133       continue;
1134     }
1135     //
1136     // make Compsolid
1137     TopoDS_Shape aCS;
1138     BOPTools_AlgoTools::MakeContainer(TopAbs_COMPSOLID, aCS);
1139     //
1140     aIt.Initialize(aCB);
1141     for (; aIt.More(); aIt.Next()) {
1142       aBB.Add(aCS, aIt.Value());
1143     }
1144     //
1145     aBB.Add(aResult, aCS);
1146   }
1147   //
1148   myShape = aResult;
1149 }
1150 //=======================================================================
1151 //function : IsBoundSplits
1152 //purpose  : 
1153 //=======================================================================
1154 Standard_Boolean BOPAlgo_BOP::IsBoundSplits
1155   (const TopoDS_Shape& aS,
1156    BOPCol_IndexedDataMapOfShapeListOfShape& aMEF)
1157 {
1158   Standard_Boolean bRet = Standard_False;
1159   if (mySplits.IsBound(aS) || myOrigins.IsBound(aS)) {
1160     return !bRet;
1161   }
1162
1163   BOPCol_ListIteratorOfListOfShape aIt;
1164   Standard_Integer aNbLS;
1165   TopAbs_Orientation anOr;
1166   //
1167   //check face aF may be connected to face from mySplits
1168   TopExp_Explorer aExp(aS, TopAbs_EDGE);
1169   for (; aExp.More(); aExp.Next()) {
1170     const TopoDS_Edge& aE = (*(TopoDS_Edge*)(&aExp.Current()));
1171     //
1172     anOr = aE.Orientation();
1173     if (anOr==TopAbs_INTERNAL) {
1174       continue;
1175     }
1176     //
1177     if (BRep_Tool::Degenerated(aE)) {
1178       continue;
1179     }
1180     //
1181     const BOPCol_ListOfShape& aLS=aMEF.FindFromKey(aE);
1182     aNbLS = aLS.Extent();
1183     if (!aNbLS) {
1184       continue;
1185     }
1186     //
1187     aIt.Initialize(aLS);
1188     for (; aIt.More(); aIt.Next()) {
1189       const TopoDS_Shape& aSx = aIt.Value();
1190       if (mySplits.IsBound(aSx) || myOrigins.IsBound(aS)) {
1191         return !bRet;
1192       }
1193     }
1194   }
1195   //
1196   return bRet;
1197 }
1198 //=======================================================================
1199 //function : TypeToExplore
1200 //purpose  : 
1201 //=======================================================================
1202 TopAbs_ShapeEnum TypeToExplore(const Standard_Integer theDim)
1203 {
1204   TopAbs_ShapeEnum aRet;
1205   //
1206   switch(theDim) {
1207   case 0:
1208     aRet=TopAbs_VERTEX;
1209     break;
1210   case 1:
1211     aRet=TopAbs_EDGE;
1212     break;
1213   case 2:
1214     aRet=TopAbs_FACE;
1215     break;
1216   case 3:
1217     aRet=TopAbs_SOLID;
1218     break;
1219   default:
1220     aRet=TopAbs_SHAPE;
1221     break;
1222   }
1223   return aRet;
1224 }
1225 //=======================================================================
1226 //function : CollectContainers
1227 //purpose  : 
1228 //=======================================================================
1229 void CollectContainers(const TopoDS_Shape& theS,
1230                        BOPCol_ListOfShape& theLSC)
1231 {
1232   TopAbs_ShapeEnum aType = theS.ShapeType();
1233   if (aType == TopAbs_WIRE ||
1234       aType == TopAbs_SHELL ||
1235       aType == TopAbs_COMPSOLID) {
1236     theLSC.Append(theS);
1237     return;
1238   }
1239   //
1240   if (aType != TopAbs_COMPOUND) {
1241     return;
1242   }
1243   //
1244   TopoDS_Iterator aIt(theS);
1245   for (; aIt.More(); aIt.Next()) {
1246     const TopoDS_Shape& aS = aIt.Value();
1247     CollectContainers(aS, theLSC);
1248   }
1249 }
1250
1251 //=======================================================================
1252 //function : RemoveDuplicates
1253 //purpose  : Filters the containers with identical contents
1254 //=======================================================================
1255 void RemoveDuplicates(BOPCol_ListOfShape& theContainers)
1256 {
1257   RemoveDuplicates(theContainers, TopAbs_WIRE);
1258   RemoveDuplicates(theContainers, TopAbs_SHELL);
1259   RemoveDuplicates(theContainers, TopAbs_COMPSOLID);
1260 }
1261
1262 //=======================================================================
1263 //function : RemoveDuplicates
1264 //purpose  : Filters the containers of given type with identical contents
1265 //=======================================================================
1266 void RemoveDuplicates(BOPCol_ListOfShape& theContainers,
1267                       const TopAbs_ShapeEnum theType)
1268 {
1269   // get containers of given type
1270   BOPCol_ListOfShape aLC;
1271   BOPCol_ListIteratorOfListOfShape aItLC(theContainers);
1272   for (; aItLC.More(); aItLC.Next()) {
1273     const TopoDS_Shape& aC = aItLC.Value();
1274     if (aC.ShapeType() == theType) {
1275       aLC.Append(aC);
1276     }
1277   }
1278   //
1279   if (aLC.IsEmpty()) {
1280     return;
1281   }
1282   //
1283   // map containers to compare its contents
1284   NCollection_IndexedDataMap<TopoDS_Shape, BOPCol_MapOfShape> aContents;
1285   //
1286   aItLC.Initialize(aLC);
1287   for (; aItLC.More(); aItLC.Next()) {
1288     const TopoDS_Shape& aC = aItLC.Value();
1289     //
1290     BOPCol_MapOfShape& aMC = aContents(aContents.Add(aC, BOPCol_MapOfShape()));
1291     //
1292     TopoDS_Iterator aIt(aC);
1293     for (; aIt.More(); aIt.Next()) {
1294       aMC.Add(aIt.Value());
1295     }
1296   }
1297   //
1298   // compare the contents of the containers and find duplicates
1299   BOPCol_MapOfShape aDuplicates;
1300   //
1301   Standard_Integer i, j, aNb = aContents.Extent();
1302   for (i = 1; i <= aNb; ++i) {
1303     const TopoDS_Shape& aCi = aContents.FindKey(i);
1304     if (aDuplicates.Contains(aCi)) {
1305       continue;
1306     }
1307     const BOPCol_MapOfShape& aMi = aContents(i);
1308     Standard_Integer aNbi = aMi.Extent();
1309     //
1310     for (j = i + 1; j <= aNb; ++j) {
1311       const TopoDS_Shape& aCj = aContents.FindKey(j);
1312       if (aDuplicates.Contains(aCj)) {
1313         continue;
1314       }
1315       const BOPCol_MapOfShape& aMj = aContents(j);
1316       Standard_Integer aNbj = aMj.Extent();
1317       //
1318       Standard_Integer aNbCommon = NbCommonItemsInMap(aMi, aMj);
1319       //
1320       if (aNbj == aNbCommon) {
1321         aDuplicates.Add(aCj);
1322         continue;
1323       }
1324       //
1325       if (aNbi == aNbCommon) {
1326         aDuplicates.Add(aCi);
1327         break;
1328       }
1329     }
1330   }
1331   //
1332   if (aDuplicates.IsEmpty()) {
1333     return;
1334   }
1335   //
1336   // remove duplicating containers
1337   aItLC.Initialize(theContainers);
1338   for (; aItLC.More(); ) {
1339     const TopoDS_Shape& aC = aItLC.Value();
1340     if (aDuplicates.Contains(aC)) {
1341       theContainers.Remove(aItLC);
1342       continue;
1343     }
1344     aItLC.Next();
1345   }
1346 }
1347
1348 //=======================================================================
1349 //function : NbCommonItemsInMap
1350 //purpose  : Counts the items contained in both maps
1351 //=======================================================================
1352 Standard_Integer NbCommonItemsInMap(const BOPCol_MapOfShape& theM1,
1353                                     const BOPCol_MapOfShape& theM2)
1354 {
1355   const BOPCol_MapOfShape* aMap1 = &theM1;
1356   const BOPCol_MapOfShape* aMap2 = &theM2;
1357   //
1358   if (theM2.Extent() < theM1.Extent()) {
1359     aMap1 = &theM2;
1360     aMap2 = &theM1;
1361   }
1362   //
1363   Standard_Integer iCommon = 0;
1364   for (BOPCol_MapIteratorOfMapOfShape aIt(*aMap1); aIt.More(); aIt.Next()) {
1365     if (aMap2->Contains(aIt.Value())) {
1366       ++iCommon;
1367     }
1368   }
1369   return iCommon;
1370 }
1371 //=======================================================================
1372 //function : MapFacesToBuildSolids
1373 //purpose  : Stores the faces of the given solid into outgoing maps:
1374 //           <theMFS> - not internal faces with reference to solid;
1375 //           <theMFI> - internal faces.
1376 //=======================================================================
1377 void MapFacesToBuildSolids(const TopoDS_Shape& theSol,
1378                            BOPCol_IndexedDataMapOfShapeListOfShape& theMFS,
1379                            BOPCol_IndexedMapOfShape& theMFI)
1380 {
1381   TopExp_Explorer aExp(theSol, TopAbs_FACE);
1382   for (; aExp.More(); aExp.Next()) {
1383     const TopoDS_Shape& aF = aExp.Current();
1384     //
1385     if (aF.Orientation() == TopAbs_INTERNAL) {
1386       theMFI.Add(aF);
1387       continue;
1388     }
1389     //
1390     BOPCol_ListOfShape* pLSol = theMFS.ChangeSeek(aF);
1391     if (!pLSol) {
1392       pLSol = &theMFS(theMFS.Add(aF, BOPCol_ListOfShape()));
1393       pLSol->Append(theSol);
1394     }
1395     else {
1396       const TopoDS_Shape& aF1 = theMFS.FindKey(theMFS.FindIndex(aF));
1397       if (aF1.Orientation() != aF.Orientation()) {
1398         pLSol->Append(theSol);
1399       }
1400     }
1401   }
1402 }