Salome HOME
a63652cc2053788e49665928fde4a9ee9ade5bae
[modules/geom.git] / src / GEOMImpl / GEOMImpl_IGroupOperations.cxx
1 // Copyright (C) 2007-2023  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include <Standard_Stream.hxx>
24
25 #include <GEOMImpl_IGroupOperations.hxx>
26
27 #include <GEOMImpl_Types.hxx>
28
29 #include <GEOM_Function.hxx>
30 #include <GEOM_ISubShape.hxx>
31 #include <GEOM_PythonDump.hxx>
32
33 #include "utilities.h"
34 #include <Utils_ExceptHandlers.hxx>
35
36 #include <TFunction_DriverTable.hxx>
37 #include <TFunction_Driver.hxx>
38 #include <TDF_Tool.hxx>
39 #include <TDataStd_Integer.hxx>
40
41 #include <TopExp.hxx>
42 #include <TopExp_Explorer.hxx>
43 #include <TopTools_IndexedMapOfShape.hxx>
44
45 #include <TColStd_HArray1OfInteger.hxx>
46 #include <TColStd_MapOfInteger.hxx>
47 #include <TColStd_ListOfInteger.hxx>
48 #include <TColStd_ListIteratorOfListOfInteger.hxx>
49
50 //=============================================================================
51 /*!
52  *   constructor:
53  */
54 //=============================================================================
55 GEOMImpl_IGroupOperations::GEOMImpl_IGroupOperations (GEOM_Engine* theEngine)
56 : GEOM_IOperations(theEngine)
57 {
58   MESSAGE("GEOMImpl_IGroupOperations::GEOMImpl_IGroupOperations");
59 }
60
61 //=============================================================================
62 /*!
63  *  destructor
64  */
65 //=============================================================================
66 GEOMImpl_IGroupOperations::~GEOMImpl_IGroupOperations()
67 {
68   MESSAGE("GEOMImpl_IGroupOperations::~GEOMImpl_IGroupOperations");
69 }
70
71
72 //=============================================================================
73 /*!
74  *  CreateGroup
75  */
76 //=============================================================================
77 Handle(GEOM_Object) GEOMImpl_IGroupOperations::CreateGroup
78        (Handle(GEOM_Object) theMainShape, TopAbs_ShapeEnum theShapeType)
79 {
80   SetErrorCode(KO);
81
82   if ( theShapeType != TopAbs_VERTEX && theShapeType != TopAbs_EDGE &&
83        theShapeType != TopAbs_FACE && theShapeType != TopAbs_SOLID ) {
84     SetErrorCode( "Error: You could create group of only next type: vertex, edge, face or solid" );
85     return NULL;
86   }
87
88   Handle(TColStd_HArray1OfInteger) anArray = new TColStd_HArray1OfInteger(1,1);
89   anArray->SetValue(1, -1);
90
91   //Add a new Sub-shape object
92   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(theMainShape, anArray);
93
94   //Set a GROUP type
95   aGroup->SetType(GEOM_GROUP);
96
97   //Set a sub-shape type
98   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
99   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)theShapeType);
100
101   //Make a Python command
102   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
103
104   GEOM::TPythonDump(aFunction) << aGroup
105     << " = geompy.CreateGroup(" << theMainShape << ", " << theShapeType << ")";
106
107   SetErrorCode(OK);
108   return aGroup;
109 }
110
111 //=============================================================================
112 /*!
113  *  AddObject
114  */
115 //=============================================================================
116 void GEOMImpl_IGroupOperations::AddObject(Handle(GEOM_Object) theGroup, int theSubShapeID)
117 {
118   SetErrorCode(KO);
119   if(theGroup.IsNull()) return;
120
121   if ( theGroup->GetType() != GEOM_GROUP ) {
122     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
123     return;
124   }
125
126   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
127   if(aFunction.IsNull()) return;
128
129   GEOM_ISubShape aSSI (aFunction);
130
131   // Check sub-shape index validity
132   TDF_Label aLabel = aSSI.GetMainShape()->GetOwnerEntry();
133   if (aLabel.IsRoot()) return;
134   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
135   if (aMainObj.IsNull()) return;
136   TopoDS_Shape aMainShape = aMainObj->GetValue();
137   if (aMainShape.IsNull()) return;
138
139   TopTools_IndexedMapOfShape aMapOfShapes;
140   TopExp::MapShapes(aMainShape, aMapOfShapes);
141
142   TopAbs_ShapeEnum aGroupType = GetType(theGroup);
143   TopAbs_ShapeEnum aShapeType = aMapOfShapes.FindKey(theSubShapeID).ShapeType();
144   if ( aGroupType != aShapeType ) {
145     SetErrorCode( "Error: You could perform this operation only with object the same type as the type of group." );
146     return;
147   }
148
149   if (theSubShapeID < 1 || aMapOfShapes.Extent() < theSubShapeID) {
150     SetErrorCode("Invalid sub-shape index: out of range");
151     return;
152   }
153
154   // Add sub-shape index
155   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
156   if(aSeq.IsNull()) return;
157   if(aSeq->Length() == 1 && aSeq->Value(1) == -1) {
158     aSeq->SetValue(1, theSubShapeID);
159   }
160   else {
161     Standard_Integer aLength = aSeq->Length();
162     Handle(TColStd_HArray1OfInteger) aNewSeq = new TColStd_HArray1OfInteger(1, aLength+1);
163     for(Standard_Integer i = 1; i<=aLength; i++) {
164       aNewSeq->SetValue(i, aSeq->Value(i));
165       if(aSeq->Value(i) == theSubShapeID) {
166         SetErrorCode(ALREADY_PRESENT);
167         return; //
168       }
169     }
170     aNewSeq->SetValue(aLength+1, theSubShapeID);
171     if ( aFunction->IsLastFuntion() ) {
172       aSSI.SetIndices(aNewSeq);
173     }
174     else {
175       aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
176       GEOM_ISubShape aSSI2 (aFunction);
177       aSSI2.SetIndices(aNewSeq);
178       aSSI2.SetMainShape( aSSI.GetMainShape() );
179     }
180   }
181
182   // As we do not recompute here our group, lets mark it as Modified
183   Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
184   theGroup->SetTic(aTic - 1);
185
186   //Make a Python command
187   GEOM::TPythonDump(aFunction, /*append=*/true)
188     << "geompy.AddObject(" << theGroup << ", " << theSubShapeID << ")";
189
190   SetErrorCode(OK);
191   return;
192 }
193
194 //=============================================================================
195 /*!
196  *  RemoveObject
197  */
198 //=============================================================================
199 void GEOMImpl_IGroupOperations::RemoveObject (Handle(GEOM_Object) theGroup, int theSubShapeID)
200 {
201   SetErrorCode(KO);
202   if(theGroup.IsNull()) return;
203
204   if ( theGroup->GetType() != GEOM_GROUP ) {
205     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
206     return;
207   }
208
209   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
210   if(aFunction.IsNull()) return;
211
212   GEOM_ISubShape aSSI(aFunction);
213   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
214   if(aSeq.IsNull()) return;
215
216   if(aSeq->Length() == 1 && aSeq->Value(1) == -1) {
217     SetErrorCode(NOT_EXISTS);
218     return;
219   }
220
221   Handle(TColStd_HArray1OfInteger) aNewSeq;
222   Standard_Integer aLength = aSeq->Length();
223   if(aLength == 1) {
224     if(aSeq->Value(1) != theSubShapeID) {
225       SetErrorCode(NOT_EXISTS);
226       return;
227     }
228     aNewSeq = new TColStd_HArray1OfInteger(1,1);
229     aNewSeq->SetValue(1, -1);
230   }
231   else {
232     aNewSeq = new TColStd_HArray1OfInteger(1, aLength-1);
233     Standard_Boolean isFound = Standard_False;
234     for (Standard_Integer i = 1, k = 1; i <= aLength; i++) {
235       if (aSeq->Value(i) == theSubShapeID) {
236         isFound = Standard_True;
237       } else {
238         if (k < aLength) { // this check is to avoid sequence <aNewSeq> overflow
239           aNewSeq->SetValue(k, aSeq->Value(i));
240           k++;
241         }
242       }
243     }
244
245     if (!isFound) {
246       SetErrorCode(NOT_EXISTS);
247       return;
248     }
249   }
250
251   if ( aFunction->IsLastFuntion() ) {
252     aSSI.SetIndices(aNewSeq);
253   }
254   else {
255     aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
256     GEOM_ISubShape aSSI2 (aFunction);
257     aSSI2.SetIndices(aNewSeq);
258     aSSI2.SetMainShape( aSSI.GetMainShape() );
259   }
260
261   // As we do not recompute here our group, lets mark it as Modified
262   TDF_Label aLabel = aSSI.GetMainShape()->GetOwnerEntry();
263   if (aLabel.IsRoot()) return;
264   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
265   if (aMainObj.IsNull()) return;
266   Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
267   theGroup->SetTic(aTic - 1);
268
269   //Make a Python command
270   GEOM::TPythonDump(aFunction, /*append=*/true)
271     << "geompy.RemoveObject(" << theGroup << ", " << theSubShapeID << ")";
272
273   SetErrorCode(OK);
274   return;
275 }
276
277 //=============================================================================
278 /*!
279  *  UnionList
280  */
281 //=============================================================================
282 void GEOMImpl_IGroupOperations::UnionList (Handle(GEOM_Object) theGroup,
283                                            const Handle(TColStd_HSequenceOfTransient)& theSubShapes)
284 {
285   SetErrorCode(KO);
286   if (theGroup.IsNull()) return;
287
288   if ( theGroup->GetType() != GEOM_GROUP ) {
289     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
290     return;
291   }
292
293   Standard_Integer aLen = theSubShapes->Length();
294   if (aLen < 1) {
295     //SetErrorCode("The list is empty");
296     SetErrorCode(OK);
297     return;
298   }
299
300   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
301   if (aFunction.IsNull()) return;
302
303   GEOM_ISubShape aSSI (aFunction);
304
305   // New contents of the group
306   TColStd_ListOfInteger aNewIDs;
307   TColStd_MapOfInteger mapIDs;
308
309   // Add current IDs to the list
310   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
311   if (aSeq.IsNull()) return;
312   Standard_Integer val_j, aLength = aSeq->Length();
313
314   for (Standard_Integer j = 1; j <= aLength; j++) {
315     val_j = aSeq->Value(j);
316     if (val_j > 0 && mapIDs.Add(val_j)) {
317       aNewIDs.Append(val_j);
318     }
319   }
320
321   // Get Main Shape
322   Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
323   if (aMainShapeFunc.IsNull()) return;
324   TDF_Label aLabel = aMainShapeFunc->GetOwnerEntry();
325   if (aLabel.IsRoot()) return;
326   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
327   if (aMainObj.IsNull()) return;
328   TopoDS_Shape aMainShape = aMainObj->GetValue();
329   if (aMainShape.IsNull()) return;
330
331   TopTools_IndexedMapOfShape mapIndices;
332   TopExp::MapShapes(aMainShape, mapIndices);
333
334   // Get group type
335   TopAbs_ShapeEnum aType = GetType(theGroup);
336
337   // Get IDs of sub-shapes to add
338   Standard_Integer i, new_id;
339   for (i = 1; i <= aLen; i++) {
340     Handle(GEOM_Object) anObj_i = Handle(GEOM_Object)::DownCast(theSubShapes->Value(i));
341
342     TopoDS_Shape aShape_i = anObj_i->GetValue();
343     if ( aShape_i.IsNull() ) continue;
344     TopAbs_ShapeEnum aType_i = aShape_i.ShapeType();
345
346     // 1. If aShape_i is sub-shape of aMainShape - add it
347     if (anObj_i->IsMainShape()) {
348       if (aType_i != aType && aType != TopAbs_SHAPE && aType != TopAbs_COMPOUND) {
349         SetErrorCode("Operation aborted: one of given objects has a wrong type");
350         return;
351       }
352       if (!mapIndices.Contains(aShape_i)) {
353         SetErrorCode("Operation aborted: not a sub-shape given");
354         return;
355       }
356       new_id = mapIndices.FindIndex(aShape_i);
357       if (mapIDs.Add(new_id)) {
358         aNewIDs.Append(new_id);
359       }
360     }
361     // 2. If type of group is not defined - add all sub-shapes of aShape_i
362     else if (aType == TopAbs_SHAPE || aType == TopAbs_COMPOUND) {
363       TopTools_IndexedMapOfShape mapIndices_i;
364       TopExp::MapShapes(aShape_i, mapIndices_i);
365       Standard_Integer ii = 1, nbSubSh = mapIndices_i.Extent();
366       Standard_Boolean someGood = Standard_False;
367       for (; ii <= nbSubSh; ii++) {
368         TopoDS_Shape aSubShape_i = mapIndices_i.FindKey(ii);
369         if (mapIndices.Contains(aSubShape_i)) {
370           someGood = Standard_True;
371           new_id = mapIndices.FindIndex(aSubShape_i);
372           if (mapIDs.Add(new_id)) {
373             aNewIDs.Append(new_id);
374           }
375         }
376       }
377       if (!someGood) {
378         SetErrorCode("Operation aborted: not a sub-shape given");
379         return;
380       }
381     }
382     // 3. If type of group is defined - add all sub-shapes of aShape_i of that type
383     else {
384       TopExp_Explorer aSubShapes_i (aShape_i, aType);
385       for (; aSubShapes_i.More(); aSubShapes_i.Next()) {
386         TopoDS_Shape aSubShape_i = aSubShapes_i.Current();
387         if (!mapIndices.Contains(aSubShape_i)) {
388           SetErrorCode("Operation aborted: not a sub-shape given");
389           return;
390         }
391         new_id = mapIndices.FindIndex(aSubShape_i);
392         if (mapIDs.Add(new_id)) {
393           aNewIDs.Append(new_id);
394         }
395       }
396     }
397   }
398
399   if (aNewIDs.Extent() > 0) {
400     Standard_Integer k = 1;
401     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
402     Handle(TColStd_HArray1OfInteger) aNewSeq = new TColStd_HArray1OfInteger(1, aNewIDs.Extent());
403     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
404       aNewSeq->SetValue(k, aNewIDsIter.Value());
405     }
406     if ( aFunction->IsLastFuntion() ) {
407       aSSI.SetIndices(aNewSeq);
408     }
409     else {
410       aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
411       GEOM_ISubShape aSSI2 (aFunction);
412       aSSI2.SetIndices(aNewSeq);
413       aSSI2.SetMainShape(aMainShapeFunc);
414     }
415     // As we do not recompute here our group, lets mark it as Modified
416     Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
417     theGroup->SetTic(aTic - 1);
418   }
419
420   //Make a Python command
421   GEOM::TPythonDump pd (aFunction, /*append=*/true);
422   pd << "geompy.UnionList(" << theGroup << ", [";
423
424   for (i = 1; i <= aLen; i++) {
425     Handle(GEOM_Object) anObj_i = Handle(GEOM_Object)::DownCast(theSubShapes->Value(i));
426     pd << anObj_i << (( i < aLen ) ? ", " : "])");
427   }
428
429   SetErrorCode(OK);
430 }
431
432 //=============================================================================
433 /*!
434  *  DifferenceList
435  */
436 //=============================================================================
437 void GEOMImpl_IGroupOperations::DifferenceList (Handle(GEOM_Object) theGroup,
438                                                 const Handle(TColStd_HSequenceOfTransient)& theSubShapes)
439 {
440   SetErrorCode(KO);
441   if (theGroup.IsNull()) return;
442
443   if ( theGroup->GetType() != GEOM_GROUP ) {
444     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
445     return;
446   }
447
448   Standard_Integer aLen = theSubShapes->Length();
449   if (aLen < 1) {
450     //SetErrorCode("The list is empty");
451     SetErrorCode(OK);
452     return;
453   }
454
455   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
456   if (aFunction.IsNull()) return;
457
458   GEOM_ISubShape aSSI (aFunction);
459
460   // Map of IDs to be removed
461   TColStd_MapOfInteger mapIDsToRemove;
462
463   // Map of current IDs
464   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
465   if (aSeq.IsNull()) return;
466   Standard_Integer aLength = aSeq->Length();
467
468   // VSR 28/04/2011 commented to allow operation even for empty group
469   //   if (aLength == 1 && aSeq->Value(1) == -1) // empty group
470   //     return;
471
472   TColStd_MapOfInteger mapIDsCurrent;
473   Standard_Integer j = 1;
474   for (; j <= aLength; j++) {
475     mapIDsCurrent.Add(aSeq->Value(j));
476   }
477
478   // Get Main Shape
479   Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
480   if (aMainShapeFunc.IsNull()) return;
481   TDF_Label aLabel = aMainShapeFunc->GetOwnerEntry();
482   if (aLabel.IsRoot()) return;
483   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
484   if (aMainObj.IsNull()) return;
485   TopoDS_Shape aMainShape = aMainObj->GetValue();
486   if (aMainShape.IsNull()) return;
487
488   TopTools_IndexedMapOfShape mapIndices;
489   TopExp::MapShapes(aMainShape, mapIndices);
490
491   // Get group type
492   TopAbs_ShapeEnum aType = GetType(theGroup);
493
494   // Get IDs of sub-shapes to be removed
495   Standard_Integer i, rem_id;
496   for (i = 1; i <= aLen; i++) {
497     Handle(GEOM_Object) anObj_i = Handle(GEOM_Object)::DownCast(theSubShapes->Value(i));
498
499     TopoDS_Shape aShape_i = anObj_i->GetValue();
500
501     // 1. If aShape_i is sub-shape of aMainShape - remove it
502     if (mapIndices.Contains(aShape_i)) {
503       rem_id = mapIndices.FindIndex(aShape_i);
504       if (rem_id > 0 && mapIDsCurrent.Contains(rem_id)) {
505         mapIDsToRemove.Add(rem_id);
506       }
507     }
508     // 2. If type of group is not defined - remove all sub-shapes of aShape_i
509     else if (aType == TopAbs_SHAPE || aType == TopAbs_COMPOUND) {
510       TopTools_IndexedMapOfShape mapIndices_i;
511       TopExp::MapShapes(aShape_i, mapIndices_i);
512       Standard_Integer nbSubSh = mapIndices_i.Extent();
513       Standard_Integer ii = 1;
514       for (; ii <= nbSubSh; ii++) {
515         TopoDS_Shape aSubShape_i = mapIndices_i.FindKey(ii);
516         if (mapIndices.Contains(aSubShape_i)) {
517           rem_id = mapIndices.FindIndex(aSubShape_i);
518           if (rem_id > 0 && mapIDsCurrent.Contains(rem_id)) {
519             mapIDsToRemove.Add(rem_id);
520           }
521         }
522       }
523     }
524     // 3. If type of group is defined - remove all sub-shapes of aShape_i of that type
525     else {
526       TopExp_Explorer aSubShapes_i (aShape_i, aType);
527       for (; aSubShapes_i.More(); aSubShapes_i.Next()) {
528         TopoDS_Shape aSubShape_i = aSubShapes_i.Current();
529         if (mapIndices.Contains(aSubShape_i)) {
530           rem_id = mapIndices.FindIndex(aSubShape_i);
531           if (rem_id > 0 && mapIDsCurrent.Contains(rem_id)) {
532             mapIDsToRemove.Add(rem_id);
533           }
534         }
535       }
536     }
537   }
538
539   if (mapIDsToRemove.Extent() > 0) {
540     Standard_Integer k = 1, aRemLength = mapIDsToRemove.Extent();
541     Handle(TColStd_HArray1OfInteger) aNewSeq;
542     if ( aLength - aRemLength > 0 ) {
543       aNewSeq = new TColStd_HArray1OfInteger(1, aLength - aRemLength);
544       for (j = 1; j <= aLength; j++) {
545         if (!mapIDsToRemove.Contains(aSeq->Value(j))) {
546           aNewSeq->SetValue(k, aSeq->Value(j));
547           k++;
548         }
549       }
550     }
551     else {
552       aNewSeq = new TColStd_HArray1OfInteger(1,1);
553       aNewSeq->SetValue(1, -1);
554     }
555
556     if ( aFunction->IsLastFuntion() ) {
557       aSSI.SetIndices(aNewSeq);
558     }
559     else {
560       aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
561       GEOM_ISubShape aSSI2 (aFunction);
562       aSSI2.SetIndices(aNewSeq);
563       aSSI2.SetMainShape(aMainShapeFunc);
564     }
565     // As we do not recompute here our group, lets mark it as Modified
566     Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
567     theGroup->SetTic(aTic - 1);
568   }
569
570   //Make a Python command
571   GEOM::TPythonDump pd (aFunction, /*append=*/true);
572   pd << "geompy.DifferenceList(" << theGroup << ", [";
573
574   for (i = 1; i <= aLen; i++) {
575     Handle(GEOM_Object) anObj_i = Handle(GEOM_Object)::DownCast(theSubShapes->Value(i));
576     pd << anObj_i << (( i < aLen ) ? ", " : "])");
577   }
578
579   SetErrorCode(OK);
580 }
581
582 //=============================================================================
583 /*!
584  *  UnionIDs
585  */
586 //=============================================================================
587 void GEOMImpl_IGroupOperations::UnionIDs (Handle(GEOM_Object) theGroup,
588                                           const Handle(TColStd_HSequenceOfInteger)& theSubShapes)
589 {
590   SetErrorCode(KO);
591   if (theGroup.IsNull()) return;
592
593   if ( theGroup->GetType() != GEOM_GROUP ) {
594     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
595     return;
596   }
597
598   Standard_Integer aLen = theSubShapes->Length();
599   if (aLen < 1) {
600     //SetErrorCode("The list is empty");
601     SetErrorCode(OK);
602     return;
603   }
604
605   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
606   if (aFunction.IsNull()) return;
607
608   GEOM_ISubShape aSSI (aFunction);
609
610   // New contents of the group
611   TColStd_ListOfInteger aNewIDs;
612   TColStd_MapOfInteger mapIDs;
613
614   // Add current IDs to the list
615   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
616   if (aSeq.IsNull()) return;
617   Standard_Integer val_j, aLength = aSeq->Length();
618
619   for (Standard_Integer j = 1; j <= aLength; j++) {
620     val_j = aSeq->Value(j);
621     if (val_j > 0 && mapIDs.Add(val_j)) {
622       aNewIDs.Append(val_j);
623     }
624   }
625
626   // Get Main Shape
627   Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
628   if (aMainShapeFunc.IsNull()) return;
629   TDF_Label aLabel = aMainShapeFunc->GetOwnerEntry();
630   if (aLabel.IsRoot()) return;
631   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
632   if (aMainObj.IsNull()) return;
633   TopoDS_Shape aMainShape = aMainObj->GetValue();
634   if (aMainShape.IsNull()) return;
635
636   TopTools_IndexedMapOfShape mapIndices;
637   TopExp::MapShapes(aMainShape, mapIndices);
638
639   // Get group type
640   TopAbs_ShapeEnum aType = GetType(theGroup);
641
642   // Get IDs of sub-shapes to add
643   Standard_Integer i, new_id;
644   for (i = 1; i <= aLen; i++) {
645     new_id = theSubShapes->Value(i);
646
647     if (0 < new_id && new_id <= mapIndices.Extent()) {
648       //if (mapIDs.Add(new_id)) { IPAL21297. Why we ignore invalid ids silently?
649       if (mapIDs.Add(new_id) && mapIndices(new_id).ShapeType()==aType ) {
650         aNewIDs.Append(new_id);
651       }
652     }
653   }
654
655   if (aNewIDs.Extent() > 0) {
656     Standard_Integer k = 1;
657     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
658     Handle(TColStd_HArray1OfInteger) aNewSeq = new TColStd_HArray1OfInteger(1, aNewIDs.Extent());
659     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
660       aNewSeq->SetValue(k, aNewIDsIter.Value());
661     }
662     if ( aFunction->IsLastFuntion() ) {
663       aSSI.SetIndices(aNewSeq);
664     }
665     else {
666       aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
667       GEOM_ISubShape aSSI2 (aFunction);
668       aSSI2.SetIndices(aNewSeq);
669       aSSI2.SetMainShape(aMainShapeFunc);
670     }
671     // As we do not recompute here our group, lets mark it as Modified
672     Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
673     theGroup->SetTic(aTic - 1);
674   }
675
676   //Make a Python command
677   GEOM::TPythonDump pd (aFunction, /*append=*/true);
678   pd << "geompy.UnionIDs(" << theGroup << ", [";
679   for (i = 1; i < aLen; i++)
680     pd << theSubShapes->Value(i) << ", ";
681   pd << theSubShapes->Value(aLen) << "])";
682
683   SetErrorCode(OK);
684 }
685
686 //=============================================================================
687 /*!
688  *  DifferenceIDs
689  */
690 //=============================================================================
691 void GEOMImpl_IGroupOperations::DifferenceIDs (Handle(GEOM_Object) theGroup,
692                                                const Handle(TColStd_HSequenceOfInteger)& theSubShapes)
693 {
694   SetErrorCode(KO);
695   if (theGroup.IsNull()) return;
696
697   if ( theGroup->GetType() != GEOM_GROUP ) {
698     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
699     return;
700   }
701
702   Standard_Integer aLen = theSubShapes->Length();
703   if (aLen < 1) {
704     //SetErrorCode("The list is empty");
705     SetErrorCode(OK);
706     return;
707   }
708
709   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
710   if (aFunction.IsNull()) return;
711
712   GEOM_ISubShape aSSI (aFunction);
713
714   // Map of IDs to be removed
715   TColStd_MapOfInteger mapIDsToRemove;
716
717   // Map of current IDs
718   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
719   if (aSeq.IsNull()) return;
720   Standard_Integer aLength = aSeq->Length();
721
722   // VSR 28/04/2011 commented to allow operation even for empty group
723   //   if (aLength == 1 && aSeq->Value(1) == -1) // empty group
724   //     return;
725
726   TColStd_MapOfInteger mapIDsCurrent;
727   Standard_Integer j = 1;
728   for (; j <= aLength; j++) {
729     mapIDsCurrent.Add(aSeq->Value(j));
730   }
731
732   // Get Main Shape
733   Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
734   if (aMainShapeFunc.IsNull()) return;
735   TDF_Label aLabel = aMainShapeFunc->GetOwnerEntry();
736   if (aLabel.IsRoot()) return;
737   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
738   if (aMainObj.IsNull()) return;
739   TopoDS_Shape aMainShape = aMainObj->GetValue();
740   if (aMainShape.IsNull()) return;
741
742   TopTools_IndexedMapOfShape mapIndices;
743   TopExp::MapShapes(aMainShape, mapIndices);
744
745   // Get IDs of sub-shapes to be removed
746   Standard_Integer i, rem_id;
747   for (i = 1; i <= aLen; i++) {
748     rem_id = theSubShapes->Value(i);
749     if (rem_id > 0 && mapIDsCurrent.Contains(rem_id)) {
750       mapIDsToRemove.Add(rem_id);
751     }
752   }
753
754   if (mapIDsToRemove.Extent() > 0) {
755     Standard_Integer k = 1, aRemLength = mapIDsToRemove.Extent();
756     Handle(TColStd_HArray1OfInteger) aNewSeq;
757     if ( aLength - aRemLength > 0 ) {
758       aNewSeq = new TColStd_HArray1OfInteger(1, aLength - aRemLength);
759       for (j = 1; j <= aLength; j++) {
760         if (!mapIDsToRemove.Contains(aSeq->Value(j))) {
761           aNewSeq->SetValue(k, aSeq->Value(j));
762           k++;
763         }
764       }
765     }
766     else {
767       aNewSeq = new TColStd_HArray1OfInteger(1,1);
768       aNewSeq->SetValue(1, -1);
769     }
770     if ( aFunction->IsLastFuntion() ) {
771       aSSI.SetIndices(aNewSeq);
772     }
773     else {
774       aFunction = theGroup->AddFunction( GEOM_Object::GetSubShapeID(), 0, true );
775       GEOM_ISubShape aSSI2 (aFunction);
776       aSSI2.SetIndices(aNewSeq);
777       aSSI2.SetMainShape(aMainShapeFunc);
778     }
779     // As we do not recompute here our group, lets mark it as Modified
780     Standard_Integer aTic = aMainObj->GetTic(); // tic of main shape
781     theGroup->SetTic(aTic - 1);
782   }
783
784   //Make a Python command
785   GEOM::TPythonDump pd (aFunction, /*append=*/true);
786   pd << "geompy.DifferenceIDs(" << theGroup << ", [";
787   for (i = 1; i < aLen; i++)
788     pd << theSubShapes->Value(i) << ", ";
789   pd << theSubShapes->Value(aLen) << "])";
790
791   SetErrorCode(OK);
792 }
793
794 //=============================================================================
795 /*!
796  *  UnionGroups
797  */
798 //=============================================================================
799 Handle(GEOM_Object) GEOMImpl_IGroupOperations::UnionGroups (Handle(GEOM_Object) theGroup1,
800                                                             Handle(GEOM_Object) theGroup2)
801 {
802   SetErrorCode(KO);
803   if (theGroup1.IsNull() || theGroup2.IsNull()) return NULL;
804
805   if ( theGroup1->GetType() != GEOM_GROUP || theGroup2->GetType() != GEOM_GROUP ) {
806     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
807     return NULL;
808   }
809
810   // Get group type
811   TopAbs_ShapeEnum aType1 = GetType(theGroup1);
812   TopAbs_ShapeEnum aType2 = GetType(theGroup2);
813   TopAbs_ShapeEnum aType = aType1;
814   if (aType1 != aType2) {
815     if (aType1 != TopAbs_SHAPE && aType1 != TopAbs_COMPOUND) {
816       if (aType2 == TopAbs_SHAPE || aType2 == TopAbs_COMPOUND)
817         aType = aType2;
818       else {
819         SetErrorCode("Error: UnionGroups cannot be performed on groups of different type");
820         return NULL;
821       }
822     }
823   }
824
825   // Get Main Shape
826   Handle(GEOM_Function) aFunction1 = theGroup1->GetLastFunction();
827   Handle(GEOM_Function) aFunction2 = theGroup2->GetLastFunction();
828   if (aFunction1.IsNull() || aFunction2.IsNull()) return NULL;
829
830   GEOM_ISubShape aSSI1 (aFunction1);
831   GEOM_ISubShape aSSI2 (aFunction2);
832
833   Handle(GEOM_Function) aMainShapeFunc1 = aSSI1.GetMainShape();
834   Handle(GEOM_Function) aMainShapeFunc2 = aSSI2.GetMainShape();
835   if (aMainShapeFunc1.IsNull() || aMainShapeFunc2.IsNull()) return NULL;
836
837   TDF_Label aLabel1 = aMainShapeFunc1->GetOwnerEntry();
838   TDF_Label aLabel2 = aMainShapeFunc2->GetOwnerEntry();
839   if (aLabel1.IsRoot() || aLabel2.IsRoot()) {
840     SetErrorCode("Error: UnionGroups can be performed only on groups");
841     return NULL;
842   }
843
844   if (aLabel1 != aLabel2) {
845     SetErrorCode("Error: UnionGroups cannot be performed on groups, built on different main shapes");
846     return NULL;
847   }
848
849   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel1);
850   if (aMainObj.IsNull()) return NULL;
851
852   // New contents of the group
853   TColStd_ListOfInteger aNewIDs;
854   TColStd_MapOfInteger mapIDs;
855
856   Handle(TColStd_HArray1OfInteger) aSeq1 = aSSI1.GetIndices();
857   Handle(TColStd_HArray1OfInteger) aSeq2 = aSSI2.GetIndices();
858   if (aSeq1.IsNull() || aSeq2.IsNull()) return NULL;
859
860   Standard_Integer j, val_j;
861   Standard_Integer aLength1 = aSeq1->Length();
862   Standard_Integer aLength2 = aSeq2->Length();
863
864   for (j = 1; j <= aLength1; j++) {
865     val_j = aSeq1->Value(j);
866     if (val_j > 0 && mapIDs.Add(val_j)) {
867       aNewIDs.Append(val_j);
868     }
869   }
870   for (j = 1; j <= aLength2; j++) {
871     val_j = aSeq2->Value(j);
872     if (val_j > 0 && mapIDs.Add(val_j)) {
873       aNewIDs.Append(val_j);
874     }
875   }
876
877   if (aNewIDs.Extent() < 1) {
878     SetErrorCode("Error: UnionGroups cannot be performed on two empty groups");
879     return NULL;
880   }
881
882   // Put new indices from the list into an array
883   Standard_Integer k = 1;
884   TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
885   Handle(TColStd_HArray1OfInteger) aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
886   for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
887     aNewArr->SetValue(k, aNewIDsIter.Value());
888   }
889
890   // Create the Group
891   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
892   aGroup->SetType(GEOM_GROUP);
893   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
894   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
895
896   // Make a Python command
897   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
898   GEOM::TPythonDump(aFunction) << aGroup << " = geompy.UnionGroups("
899                                << theGroup1 << ", " << theGroup2 << ")";
900
901   SetErrorCode(OK);
902   return aGroup;
903 }
904
905 //=============================================================================
906 /*!
907  *  IntersectGroups
908  */
909 //=============================================================================
910 Handle(GEOM_Object) GEOMImpl_IGroupOperations::IntersectGroups (Handle(GEOM_Object) theGroup1,
911                                                                 Handle(GEOM_Object) theGroup2)
912 {
913   SetErrorCode(KO);
914   if (theGroup1.IsNull() || theGroup2.IsNull()) return NULL;
915
916   if ( theGroup1->GetType() != GEOM_GROUP || theGroup2->GetType() != GEOM_GROUP ) {
917     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
918     return NULL;
919   }
920
921   // Get group type
922   TopAbs_ShapeEnum aType1 = GetType(theGroup1);
923   TopAbs_ShapeEnum aType2 = GetType(theGroup2);
924   TopAbs_ShapeEnum aType = aType1;
925   if (aType1 != aType2) {
926     if (aType1 != TopAbs_SHAPE && aType1 != TopAbs_COMPOUND) {
927       if (aType2 == TopAbs_SHAPE || aType2 == TopAbs_COMPOUND)
928         aType = aType2;
929       else {
930         SetErrorCode("Error: IntersectGroups cannot be performed on groups of different type");
931         return NULL;
932       }
933     }
934   }
935
936   // Get Main Shape
937   Handle(GEOM_Function) aFunction1 = theGroup1->GetLastFunction();
938   Handle(GEOM_Function) aFunction2 = theGroup2->GetLastFunction();
939   if (aFunction1.IsNull() || aFunction2.IsNull()) return NULL;
940
941   GEOM_ISubShape aSSI1 (aFunction1);
942   GEOM_ISubShape aSSI2 (aFunction2);
943
944   Handle(GEOM_Function) aMainShapeFunc1 = aSSI1.GetMainShape();
945   Handle(GEOM_Function) aMainShapeFunc2 = aSSI2.GetMainShape();
946   if (aMainShapeFunc1.IsNull() || aMainShapeFunc2.IsNull()) return NULL;
947
948   TDF_Label aLabel1 = aMainShapeFunc1->GetOwnerEntry();
949   TDF_Label aLabel2 = aMainShapeFunc2->GetOwnerEntry();
950   if (aLabel1.IsRoot() || aLabel2.IsRoot()) {
951     SetErrorCode("Error: UnionGroups can be performed only on groups");
952     return NULL;
953   }
954
955   if (aLabel1 != aLabel2) {
956     SetErrorCode("Error: IntersectGroups cannot be performed on groups, built on different main shapes");
957     return NULL;
958   }
959
960   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel1);
961   if (aMainObj.IsNull()) return NULL;
962
963   // New contents of the group
964   TColStd_ListOfInteger aNewIDs;
965   TColStd_MapOfInteger mapIDs;
966
967   Handle(TColStd_HArray1OfInteger) aSeq1 = aSSI1.GetIndices();
968   Handle(TColStd_HArray1OfInteger) aSeq2 = aSSI2.GetIndices();
969   if (aSeq1.IsNull() || aSeq2.IsNull()) return NULL;
970
971   Standard_Integer j, val_j;
972   Standard_Integer aLength1 = aSeq1->Length();
973   Standard_Integer aLength2 = aSeq2->Length();
974
975   for (j = 1; j <= aLength1; j++) {
976     mapIDs.Add(aSeq1->Value(j));
977   }
978   for (j = 1; j <= aLength2; j++) {
979     val_j = aSeq2->Value(j);
980     if (val_j > 0 && !mapIDs.Add(val_j)) {
981       // add index, if it is in mapIDs (filled from Group_1)
982       aNewIDs.Append(val_j);
983     }
984   }
985
986   Handle(TColStd_HArray1OfInteger) aNewArr;
987   if (aNewIDs.Extent() < 1) {
988     aNewArr = new TColStd_HArray1OfInteger (1, 1);
989     aNewArr->SetValue(1, -1);
990   }
991   else {
992     // Put new indices from the list into an array
993     Standard_Integer k = 1;
994     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
995     aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
996     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
997       aNewArr->SetValue(k, aNewIDsIter.Value());
998     }
999   }
1000
1001   // Create the Group
1002   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
1003   aGroup->SetType(GEOM_GROUP);
1004   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
1005   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
1006
1007   // Make a Python command
1008   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
1009   GEOM::TPythonDump(aFunction) << aGroup << " = geompy.IntersectGroups("
1010                                << theGroup1 << ", " << theGroup2 << ")";
1011
1012   SetErrorCode(OK);
1013   return aGroup;
1014 }
1015
1016 //=============================================================================
1017 /*!
1018  *  CutGroups
1019  */
1020 //=============================================================================
1021 Handle(GEOM_Object) GEOMImpl_IGroupOperations::CutGroups (Handle(GEOM_Object) theGroup1,
1022                                                           Handle(GEOM_Object) theGroup2)
1023 {
1024   SetErrorCode(KO);
1025   if (theGroup1.IsNull() || theGroup2.IsNull()) return NULL;
1026
1027   if ( theGroup1->GetType() != GEOM_GROUP || theGroup2->GetType() != GEOM_GROUP ) {
1028     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1029     return NULL;
1030   }
1031
1032   // Get group type
1033   TopAbs_ShapeEnum aType1 = GetType(theGroup1);
1034   TopAbs_ShapeEnum aType2 = GetType(theGroup2);
1035   TopAbs_ShapeEnum aType = aType1;
1036   if (aType1 != aType2) {
1037     if (aType1 != TopAbs_SHAPE && aType1 != TopAbs_COMPOUND) {
1038       if (aType2 == TopAbs_SHAPE || aType2 == TopAbs_COMPOUND)
1039         aType = aType2;
1040       else {
1041         SetErrorCode("Error: CutGroups cannot be performed on groups of different type");
1042         return NULL;
1043       }
1044     }
1045   }
1046
1047   // Get Main Shape
1048   Handle(GEOM_Function) aFunction1 = theGroup1->GetLastFunction();
1049   Handle(GEOM_Function) aFunction2 = theGroup2->GetLastFunction();
1050   if (aFunction1.IsNull() || aFunction2.IsNull()) return NULL;
1051
1052   GEOM_ISubShape aSSI1 (aFunction1);
1053   GEOM_ISubShape aSSI2 (aFunction2);
1054
1055   Handle(GEOM_Function) aMainShapeFunc1 = aSSI1.GetMainShape();
1056   Handle(GEOM_Function) aMainShapeFunc2 = aSSI2.GetMainShape();
1057   if (aMainShapeFunc1.IsNull() || aMainShapeFunc2.IsNull()) return NULL;
1058
1059   TDF_Label aLabel1 = aMainShapeFunc1->GetOwnerEntry();
1060   TDF_Label aLabel2 = aMainShapeFunc2->GetOwnerEntry();
1061   if (aLabel1.IsRoot() || aLabel2.IsRoot()) {
1062     SetErrorCode("Error: UnionGroups can be performed only on groups");
1063     return NULL;
1064   }
1065
1066   if (aLabel1 != aLabel2) {
1067     SetErrorCode("Error: CutGroups cannot be performed on groups, built on different main shapes");
1068     return NULL;
1069   }
1070
1071   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel1);
1072   if (aMainObj.IsNull()) return NULL;
1073
1074   // New contents of the group
1075   TColStd_ListOfInteger aNewIDs;
1076   TColStd_MapOfInteger mapIDs;
1077
1078   Handle(TColStd_HArray1OfInteger) aSeq1 = aSSI1.GetIndices();
1079   Handle(TColStd_HArray1OfInteger) aSeq2 = aSSI2.GetIndices();
1080   if (aSeq1.IsNull() || aSeq2.IsNull()) return NULL;
1081
1082   Standard_Integer j, val_j;
1083   Standard_Integer aLength1 = aSeq1->Length();
1084   Standard_Integer aLength2 = aSeq2->Length();
1085
1086   for (j = 1; j <= aLength2; j++) {
1087     mapIDs.Add(aSeq2->Value(j));
1088   }
1089   for (j = 1; j <= aLength1; j++) {
1090     val_j = aSeq1->Value(j);
1091     if (val_j > 0 && mapIDs.Add(val_j)) {
1092       // add index, if it is not in mapIDs (filled from Group_2)
1093       aNewIDs.Append(val_j);
1094     }
1095   }
1096
1097   Handle(TColStd_HArray1OfInteger) aNewArr;
1098   if (aNewIDs.Extent() < 1) {
1099     aNewArr = new TColStd_HArray1OfInteger (1, 1);
1100     aNewArr->SetValue(1, -1);
1101   }
1102   else {
1103     // Put new indices from the list into an array
1104     Standard_Integer k = 1;
1105     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
1106     aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
1107     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
1108       aNewArr->SetValue(k, aNewIDsIter.Value());
1109     }
1110   }
1111
1112   // Create the Group
1113   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
1114   aGroup->SetType(GEOM_GROUP);
1115   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
1116   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
1117
1118   // Make a Python command
1119   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
1120   GEOM::TPythonDump(aFunction) << aGroup << " = geompy.CutGroups("
1121                                << theGroup1 << ", " << theGroup2 << ")";
1122
1123   SetErrorCode(OK);
1124   return aGroup;
1125 }
1126
1127 //=============================================================================
1128 /*!
1129  *  UnionListOfGroups
1130  */
1131 //=============================================================================
1132 Handle(GEOM_Object) GEOMImpl_IGroupOperations::UnionListOfGroups
1133                          (const Handle(TColStd_HSequenceOfTransient)& theGList)
1134 {
1135   SetErrorCode(KO);
1136   if (theGList.IsNull()) return NULL;
1137
1138   Standard_Integer i, aLen = theGList->Length();
1139   if (aLen < 1) {
1140     SetErrorCode("UnionListOfGroups error: the list of groups is empty");
1141     return NULL;
1142   }
1143
1144   TopAbs_ShapeEnum aType = TopAbs_SHAPE, aType_i;   // todo: refactor: aType must be initialized to avoid warning (see below)
1145   TDF_Label aLabel, aLabel_i;
1146   TColStd_ListOfInteger aNewIDs;
1147   TColStd_MapOfInteger mapIDs;
1148
1149   // Iterate on the initial groups
1150   for (i = 1; i <= aLen; i++) {
1151     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList->Value(i));
1152     if ( aGr_i->GetType() != GEOM_GROUP ) {
1153       SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1154       return NULL;
1155     }
1156     // Get group type
1157     aType_i = GetType(aGr_i);
1158     if (i == 1)
1159       aType = aType_i;
1160     else {
1161       if (aType_i != aType) {
1162         if (aType != TopAbs_SHAPE && aType != TopAbs_COMPOUND) { // todo: refactor: aType must be initialized to avoid warning (see above)
1163           if (aType_i == TopAbs_SHAPE || aType_i == TopAbs_COMPOUND)
1164             aType = aType_i;
1165           else {
1166             SetErrorCode("Error: UnionListOfGroups cannot be performed on groups of different type");
1167             return NULL;
1168           }
1169         }
1170       }
1171     }
1172
1173     // Get Main Shape
1174     Handle(GEOM_Function) aFunction_i = aGr_i->GetLastFunction();
1175     if (aFunction_i.IsNull()) return NULL;
1176     GEOM_ISubShape aSSI (aFunction_i);
1177     Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
1178     if (aMainShapeFunc.IsNull()) return NULL;
1179     aLabel_i = aMainShapeFunc->GetOwnerEntry();
1180     if (aLabel_i.IsRoot()) {
1181       SetErrorCode("Error: UnionGroups can be performed only on groups");
1182       return NULL;
1183     }
1184     if (i == 1)
1185       aLabel = aLabel_i;
1186     else {
1187       if (aLabel_i != aLabel) {
1188         SetErrorCode("Error: UnionListOfGroups cannot be performed on groups, built on different main shapes");
1189         return NULL;
1190       }
1191     }
1192
1193     // New contents of the group
1194     Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
1195     if (aSeq.IsNull()) return NULL;
1196
1197     Standard_Integer j, val_j, aLength = aSeq->Length();
1198     for (j = 1; j <= aLength; j++) {
1199       val_j = aSeq->Value(j);
1200       if (val_j > 0 && mapIDs.Add(val_j)) {
1201         aNewIDs.Append(val_j);
1202       }
1203     }
1204   }
1205
1206   // Check the resulting list of indices
1207   if (aNewIDs.Extent() < 1) {
1208     SetErrorCode("Error: UnionListOfGroups cannot be performed on all empty groups");
1209     return NULL;
1210   }
1211
1212   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
1213   if (aMainObj.IsNull()) return NULL;
1214
1215   // Put new indices from the list into an array
1216   Standard_Integer k = 1;
1217   TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
1218   Handle(TColStd_HArray1OfInteger) aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
1219   for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
1220     aNewArr->SetValue(k, aNewIDsIter.Value());
1221   }
1222
1223   // Create the Group
1224   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
1225   aGroup->SetType(GEOM_GROUP);
1226   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
1227   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
1228
1229   //Make a Python command
1230   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
1231   GEOM::TPythonDump pd (aFunction);
1232   pd << aGroup << " = geompy.UnionListOfGroups([";
1233   for (i = 1; i <= aLen; i++) {
1234     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList->Value(i));
1235     pd << aGr_i << ((i < aLen) ? ", " : "])");
1236   }
1237
1238   SetErrorCode(OK);
1239   return aGroup;
1240 }
1241
1242 //=============================================================================
1243 /*!
1244  *  IntersectListOfGroups
1245  */
1246 //=============================================================================
1247 Handle(GEOM_Object) GEOMImpl_IGroupOperations::IntersectListOfGroups
1248                          (const Handle(TColStd_HSequenceOfTransient)& theGList)
1249 {
1250   SetErrorCode(KO);
1251   if (theGList.IsNull()) return NULL;
1252
1253   Standard_Integer i, aLen = theGList->Length();
1254   if (aLen < 1) {
1255     SetErrorCode("IntersectListOfGroups error: the list of groups is empty");
1256     return NULL;
1257   }
1258
1259   TopAbs_ShapeEnum aType = TopAbs_SHAPE, aType_i; // todo: refactor: aType must be initialized to avoid warning (see below)
1260   TDF_Label aLabel, aLabel_i;
1261   TColStd_ListOfInteger aNewIDs;
1262   TColStd_MapOfInteger mapIDs;
1263
1264   // Iterate on the initial groups
1265   for (i = 1; i <= aLen; i++) {
1266     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList->Value(i));
1267     if ( aGr_i->GetType() != GEOM_GROUP ) {
1268       SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1269       return NULL;
1270     }
1271     // Get group type
1272     aType_i = GetType(aGr_i);
1273     if (i == 1)
1274       aType = aType_i;
1275     else {
1276       if (aType_i != aType) {
1277         if (aType != TopAbs_SHAPE && aType != TopAbs_COMPOUND) { // todo: refactor: aType must be initialized to avoid warning (see above)
1278           if (aType_i == TopAbs_SHAPE || aType_i == TopAbs_COMPOUND)
1279             aType = aType_i;
1280           else {
1281             SetErrorCode("Error: IntersectListOfGroups cannot be performed on groups of different type");
1282             return NULL;
1283           }
1284         }
1285       }
1286     }
1287
1288     // Get Main Shape
1289     Handle(GEOM_Function) aFunction_i = aGr_i->GetLastFunction();
1290     if (aFunction_i.IsNull()) return NULL;
1291     GEOM_ISubShape aSSI (aFunction_i);
1292     Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
1293     if (aMainShapeFunc.IsNull()) return NULL;
1294     aLabel_i = aMainShapeFunc->GetOwnerEntry();
1295     if (aLabel_i.IsRoot()) {
1296      SetErrorCode("Error: UnionGroups can be performed only on groups");
1297      return NULL;
1298     }
1299     if (i == 1)
1300       aLabel = aLabel_i;
1301     else {
1302       if (aLabel_i != aLabel) {
1303         SetErrorCode("Error: IntersectListOfGroups cannot be performed on groups, built on different main shapes");
1304         return NULL;
1305       }
1306     }
1307
1308     // New contents of the group
1309     Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
1310     if (aSeq.IsNull()) return NULL;
1311
1312     Standard_Integer j, val_j, aLength = aSeq->Length();
1313     for (j = 1; j <= aLength; j++) {
1314       val_j = aSeq->Value(j);
1315       if (val_j > 0) {
1316         if (i == 1) {
1317           // get all elements of the first group
1318           if (mapIDs.Add(val_j))
1319             aNewIDs.Append(val_j);
1320         }
1321         else {
1322           // get only elements, present in all previously processed groups
1323           if (!mapIDs.Add(val_j))
1324             aNewIDs.Append(val_j);
1325         }
1326       }
1327     }
1328
1329     // refill the map with only validated elements
1330     if (i > 1) {
1331       mapIDs.Clear();
1332       TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
1333       for (; aNewIDsIter.More(); aNewIDsIter.Next()) {
1334         mapIDs.Add(aNewIDsIter.Value());
1335       }
1336     }
1337     // clear the resulting list before starting the next sycle
1338     if (i < aLen) {
1339       aNewIDs.Clear();
1340     }
1341   }
1342
1343   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
1344   if (aMainObj.IsNull()) return NULL;
1345
1346   Handle(TColStd_HArray1OfInteger) aNewArr;
1347   if (aNewIDs.Extent() < 1) {
1348     aNewArr = new TColStd_HArray1OfInteger (1, 1);
1349     aNewArr->SetValue(1, -1);
1350   }
1351   else {
1352     // Put new indices from the list into an array
1353     Standard_Integer k = 1;
1354     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
1355     aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
1356     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
1357       aNewArr->SetValue(k, aNewIDsIter.Value());
1358     }
1359   }
1360
1361   // Create the Group
1362   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
1363   aGroup->SetType(GEOM_GROUP);
1364   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
1365   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
1366
1367   //Make a Python command
1368   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
1369   GEOM::TPythonDump pd (aFunction);
1370   pd << aGroup << " = geompy.IntersectListOfGroups([";
1371   for (i = 1; i <= aLen; i++) {
1372     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList->Value(i));
1373     pd << aGr_i << ((i < aLen) ? ", " : "])");
1374   }
1375
1376   SetErrorCode(OK);
1377   return aGroup;
1378 }
1379
1380 //=============================================================================
1381 /*!
1382  *  CutListOfGroups
1383  */
1384 //=============================================================================
1385 Handle(GEOM_Object) GEOMImpl_IGroupOperations::CutListOfGroups
1386                         (const Handle(TColStd_HSequenceOfTransient)& theGList1,
1387                          const Handle(TColStd_HSequenceOfTransient)& theGList2)
1388 {
1389   SetErrorCode(KO);
1390   if (theGList1.IsNull() || theGList2.IsNull()) return NULL;
1391
1392   Standard_Integer i;
1393   Standard_Integer aLen1 = theGList1->Length();
1394   Standard_Integer aLen2 = theGList2->Length();
1395   if (aLen1 < 1) {
1396     SetErrorCode("CutListOfGroups error: the first list of groups is empty");
1397     return NULL;
1398   }
1399
1400   TopAbs_ShapeEnum aType = TopAbs_SHAPE, aType_i; // todo: refactor: aType must be initialized to avoid warning (see below)
1401   TDF_Label aLabel, aLabel_i;
1402   TColStd_ListOfInteger aNewIDs;
1403   TColStd_MapOfInteger mapIDs;
1404
1405   // 1. Collect indices to be excluded (from theGList2) into the mapIDs
1406   for (i = 1; i <= aLen2; i++) {
1407     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList2->Value(i));
1408     if ( aGr_i->GetType() != GEOM_GROUP ) {
1409       SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1410       return NULL;
1411     }
1412     // Get group type
1413     aType_i = GetType(aGr_i);
1414     if (i == 1)
1415       aType = aType_i;
1416     else {
1417       if (aType_i != aType) {
1418         if (aType != TopAbs_SHAPE && aType != TopAbs_COMPOUND) {
1419           if (aType_i == TopAbs_SHAPE || aType_i == TopAbs_COMPOUND)
1420             aType = aType_i;
1421           else {
1422             SetErrorCode("Error: CutListOfGroups cannot be performed on groups of different type");
1423             return NULL;
1424           }
1425         }
1426       }
1427     }
1428
1429     // Get Main Shape
1430     Handle(GEOM_Function) aFunction_i = aGr_i->GetLastFunction();
1431     if (aFunction_i.IsNull()) return NULL;
1432     GEOM_ISubShape aSSI (aFunction_i);
1433     Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
1434     if (aMainShapeFunc.IsNull()) return NULL;
1435     aLabel_i = aMainShapeFunc->GetOwnerEntry();
1436     if (aLabel_i.IsRoot()) {
1437       SetErrorCode("Error: UnionGroups can be performed only on groups");
1438       return NULL;
1439     }
1440     if (i == 1)
1441       aLabel = aLabel_i;
1442     else {
1443       if (aLabel_i != aLabel) {
1444         SetErrorCode("Error: CutListOfGroups cannot be performed on groups, built on different main shapes");
1445         return NULL;
1446       }
1447     }
1448
1449     // Indiced to exclude
1450     Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
1451     if (aSeq.IsNull()) return NULL;
1452
1453     Standard_Integer j, aLength = aSeq->Length();
1454     for (j = 1; j <= aLength; j++) {
1455       mapIDs.Add(aSeq->Value(j));
1456     }
1457   }
1458
1459   // 2. Collect indices from theGList1, avoiding indices from theGList2 (mapIDs)
1460   for (i = 1; i <= aLen1; i++) {
1461     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList1->Value(i));
1462     if ( aGr_i->GetType() != GEOM_GROUP ) {
1463       SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1464       return NULL;
1465     }
1466     // Get group type
1467     aType_i = GetType(aGr_i);
1468     if (i == 1 && aLen2 < 1)
1469       aType = aType_i;
1470     else {
1471       if (aType_i != aType) {
1472         if (aType != TopAbs_SHAPE && aType != TopAbs_COMPOUND) { // todo: refactor: aType must be initialized to avoid warning (see above)
1473           if (aType_i == TopAbs_SHAPE || aType_i == TopAbs_COMPOUND)
1474             aType = aType_i;
1475           else {
1476             SetErrorCode("Error: CutListOfGroups cannot be performed on groups of different type");
1477             return NULL;
1478           }
1479         }
1480       }
1481     }
1482
1483     // Get Main Shape
1484     Handle(GEOM_Function) aFunction_i = aGr_i->GetLastFunction();
1485     if (aFunction_i.IsNull()) return NULL;
1486     GEOM_ISubShape aSSI (aFunction_i);
1487     Handle(GEOM_Function) aMainShapeFunc = aSSI.GetMainShape();
1488     if (aMainShapeFunc.IsNull()) return NULL;
1489     aLabel_i = aMainShapeFunc->GetOwnerEntry();
1490     if (aLabel_i.IsRoot()) {
1491       SetErrorCode("Error: UnionGroups can be performed only on groups");
1492       return NULL;
1493     }
1494     if (i == 1 && aLen2 < 1)
1495       aLabel = aLabel_i;
1496     else {
1497       if (aLabel_i != aLabel) {
1498         SetErrorCode("Error: CutListOfGroups cannot be performed on groups, built on different main shapes");
1499         return NULL;
1500       }
1501     }
1502
1503     // New contents of the group
1504     Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
1505     if (aSeq.IsNull()) return NULL;
1506
1507     Standard_Integer j, val_j, aLength = aSeq->Length();
1508     for (j = 1; j <= aLength; j++) {
1509       val_j = aSeq->Value(j);
1510       if (val_j > 0 && mapIDs.Add(val_j)) {
1511         // get only elements, not present in mapIDs (theGList2)
1512         aNewIDs.Append(val_j);
1513       }
1514     }
1515   }
1516
1517   Handle(GEOM_Object) aMainObj = GEOM_Object::GetObject(aLabel);
1518   if (aMainObj.IsNull()) return NULL;
1519
1520   Handle(TColStd_HArray1OfInteger) aNewArr;
1521   if (aNewIDs.Extent() < 1) {
1522     aNewArr = new TColStd_HArray1OfInteger (1, 1);
1523     aNewArr->SetValue(1, -1);
1524   }
1525   else {
1526     // Put new indices from the list into an array
1527     Standard_Integer k = 1;
1528     TColStd_ListIteratorOfListOfInteger aNewIDsIter (aNewIDs);
1529     aNewArr = new TColStd_HArray1OfInteger (1, aNewIDs.Extent());
1530     for (; aNewIDsIter.More(); aNewIDsIter.Next(), k++) {
1531       aNewArr->SetValue(k, aNewIDsIter.Value());
1532     }
1533   }
1534
1535   // Create the Group
1536   Handle(GEOM_Object) aGroup = GetEngine()->AddSubShape(aMainObj, aNewArr);
1537   aGroup->SetType(GEOM_GROUP);
1538   TDF_Label aFreeLabel = aGroup->GetFreeLabel();
1539   TDataStd_Integer::Set(aFreeLabel, (Standard_Integer)aType);
1540
1541   //Make a Python command
1542   Handle(GEOM_Function) aFunction = aGroup->GetFunction(1);
1543   GEOM::TPythonDump pd (aFunction);
1544   pd << aGroup << " = geompy.CutListOfGroups([";
1545   for (i = 1; i <= aLen1; i++) {
1546     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList1->Value(i));
1547     pd << aGr_i << ((i < aLen1) ? ", " : "], [");
1548   }
1549   for (i = 1; i <= aLen2; i++) {
1550     Handle(GEOM_Object) aGr_i = Handle(GEOM_Object)::DownCast(theGList2->Value(i));
1551     pd << aGr_i << ((i < aLen2) ? ", " : "])");
1552   }
1553
1554   SetErrorCode(OK);
1555   return aGroup;
1556 }
1557
1558 //=============================================================================
1559 /*!
1560  *  GetType
1561  */
1562 //=============================================================================
1563 TopAbs_ShapeEnum GEOMImpl_IGroupOperations::GetType(Handle(GEOM_Object) theGroup)
1564 {
1565   SetErrorCode(KO);
1566
1567   TDF_Label aFreeLabel = theGroup->GetFreeLabel();
1568   Handle(TDataStd_Integer) anAttrib;
1569   if(!aFreeLabel.FindAttribute(TDataStd_Integer::GetID(), anAttrib)) return TopAbs_SHAPE;
1570
1571   SetErrorCode(OK);
1572   return (TopAbs_ShapeEnum) anAttrib->Get();
1573 }
1574
1575 //=============================================================================
1576 /*!
1577  *  GetMainShape
1578  */
1579 //=============================================================================
1580 Handle(GEOM_Object) GEOMImpl_IGroupOperations::GetMainShape (Handle(GEOM_Object) theGroup)
1581 {
1582   SetErrorCode(KO);
1583
1584   if (theGroup.IsNull()) return NULL;
1585   if (theGroup->GetType() != GEOM_GROUP &&
1586       theGroup->GetType() != GEOM_SUBSHAPE) {
1587     SetErrorCode("Error: You could perform this operation only with a group or a sub-shape.");
1588     return NULL;
1589   }
1590
1591   Handle(GEOM_Function) aGroupFunction = theGroup->GetFunction(1);
1592   if (aGroupFunction.IsNull()) return NULL;
1593
1594   GEOM_ISubShape aSSI (aGroupFunction);
1595   Handle(GEOM_Function) aMainShapeFunction = aSSI.GetMainShape();
1596   if (aMainShapeFunction.IsNull()) return NULL;
1597
1598   TDF_Label aLabel = aMainShapeFunction->GetOwnerEntry();
1599   Handle(GEOM_Object) aMainShape = GEOM_Object::GetObject(aLabel);
1600   if (aMainShape.IsNull()) return NULL;
1601
1602   //Make a Python command
1603   //GEOM::TPythonDump(aGroupFunction, /*append=*/true)
1604   //  << aMainShape << " = geompy.GetMainShape(" << theGroup << ")";
1605
1606   SetErrorCode(OK);
1607   return aMainShape;
1608 }
1609
1610 //=============================================================================
1611 /*!
1612  *  GetObjects
1613  */
1614 //=============================================================================
1615 Handle(TColStd_HArray1OfInteger) GEOMImpl_IGroupOperations::GetObjects(Handle(GEOM_Object) theGroup)
1616 {
1617   SetErrorCode(KO);
1618
1619   if(theGroup.IsNull()) return NULL;
1620   if ( theGroup->GetType() != GEOM_GROUP ) {
1621     SetErrorCode( "Error: You could perform this operation only with group. Please select a group." );
1622     return NULL;
1623   }
1624   Handle(GEOM_Function) aFunction = theGroup->GetLastFunction();
1625   if(aFunction.IsNull()) return NULL;
1626
1627   GEOM_ISubShape aSSI(aFunction);
1628   Handle(TColStd_HArray1OfInteger) aSeq = aSSI.GetIndices();
1629   if(aSeq.IsNull()) return NULL;
1630
1631   if(aSeq->Length() == 1 && aSeq->Value(1) == -1) {
1632     SetErrorCode(OK);
1633     return NULL;
1634   }
1635
1636   SetErrorCode(OK);
1637   return aSeq;
1638 }