Salome HOME
Updated copyright comment
[modules/shaper.git] / src / Selector / Selector_Algo.cpp
1 // Copyright (C) 2014-2024  CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <Selector_Algo.h>
21
22 #include <Selector_Primitive.h>
23 #include <Selector_Intersect.h>
24 #include <Selector_Modify.h>
25 #include <Selector_Container.h>
26 #include <Selector_FilterByNeighbors.h>
27 #include <Selector_WeakName.h>
28
29 #include <TDF_Tool.hxx>
30 #include <TopoDS_Face.hxx>
31 #include <TopoDS_Edge.hxx>
32 #include <TopoDS.hxx>
33 #include <TopTools_MapOfShape.hxx>
34 #include <Geom_Surface.hxx>
35 #include <BRep_Tool.hxx>
36 #include <TNaming_Builder.hxx>
37 #include <TNaming_Tool.hxx>
38 #include <TNaming_SameShapeIterator.hxx>
39 #include <TNaming_Iterator.hxx>
40 #include <TNaming_NewShapeIterator.hxx>
41 #include <TDataStd_ReferenceArray.hxx>
42 #include <TDataStd_ExtStringList.hxx>
43 #include <TDataStd_Integer.hxx>
44 #include <TDataStd_UAttribute.hxx>
45 #include <TopoDS_Iterator.hxx>
46 #include <TopExp_Explorer.hxx>
47 #include <BRep_Builder.hxx>
48
49
50 /// type of the selection, integer keeps the Selector_Type value
51 static const Standard_GUID kSEL_TYPE("db174d59-c2e3-4a90-955e-55544df090d6");
52 // geometrical naming indicator
53 static const Standard_GUID kGEOMETRICAL_NAMING("a5322d02-50fb-43ed-9255-75c7b93f6657");
54
55
56 //  reference attribute that contains the reference to labels where the "from" or "base" shapes
57 // of selection are located
58 static const Standard_GUID kBASE_ARRAY("7c515b1a-9549-493d-9946-a4933a22f45f");
59 // if the base array contains reference to the root label, this means that it refers to an
60 // external document and this list contains a tag in the document
61 static const Standard_GUID kBASE_LIST("7c515b1a-9549-493d-9946-a4933a22f45a");
62
63 Selector_Algo::Selector_Algo()
64 {
65   myGeometricalNaming = false; // default values
66   myAlwaysGeometricalNaming = false;
67 }
68
69 static TDF_Label findGoodLabelWithShape(const TDF_Label theAccess, const TopoDS_Shape& theShape) {
70   TDF_Label aResult;
71   TDF_Label aMyOpLab;
72   bool aMyOpLabIsComputed = false;
73   if (TNaming_Tool::HasLabel(theAccess, theShape)) { // selection and delete evolution are not used
74     for(TNaming_SameShapeIterator aShapes(theShape, theAccess); aShapes.More(); aShapes.Next())
75     {
76       Handle(TNaming_NamedShape) aNS;
77       if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
78         if (aNS->Evolution() == TNaming_MODIFY || aNS->Evolution() == TNaming_GENERATED ||
79             aNS->Evolution() == TNaming_PRIMITIVE) {
80           // getting label of this operation to avoid usage of ready shapes of this
81           if (!aMyOpLabIsComputed) {
82             aMyOpLab = theAccess;
83             if (!aMyOpLab.IsNull()) {
84               int aDepth = aMyOpLab.Depth();
85               if (aDepth < 3)
86                 aMyOpLab.Nullify();
87               else {
88                 for(; aDepth != 3; aDepth--)
89                   aMyOpLab = aMyOpLab.Father();
90               }
91             }
92             aMyOpLabIsComputed = true;
93           }
94           if (!aMyOpLab.IsNull() && aNS->Label().IsDescendant(aMyOpLab))
95             continue;
96           aResult = aNS->Label();
97           break;
98         }
99       }
100     }
101   }
102   return aResult;
103 }
104
105 #define SET_ALGO_FLAGS(algo) \
106   algo->myLab = theAccess; \
107   algo->myBaseDocumentLab = theBaseDocument; \
108   algo->myGeometricalNaming = theGeometricalNaming; \
109   algo->myUseNeighbors = theUseNeighbors; \
110   algo->myUseIntersections = theUseIntersections; \
111   algo->myAlwaysGeometricalNaming = theAlwaysGeometricalNaming;
112
113 Selector_Algo* Selector_Algo::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
114   const TDF_Label theAccess, const TDF_Label theBaseDocument,
115   const bool theGeometricalNaming, const bool theUseNeighbors, const bool theUseIntersections,
116   const bool theAlwaysGeometricalNaming)
117 {
118   Selector_Algo* aResult = NULL;
119   if (theValue.IsNull() || theContext.IsNull())
120     return aResult;
121
122   // check the value shape can be named as it is, or it is needed to construct it from the
123   // higher level shapes (like a box vertex by faces that form this vertex)
124   bool aIsFound = !findGoodLabelWithShape(theAccess, theValue).IsNull();
125   if (!aIsFound && !theBaseDocument.IsNull()) // searching in the base document
126     aIsFound = !findGoodLabelWithShape(theBaseDocument, theValue).IsNull();
127   if (!aIsFound) {
128     TopAbs_ShapeEnum aSelectionType = theValue.ShapeType();
129     if (aSelectionType == TopAbs_COMPOUND || aSelectionType == TopAbs_COMPSOLID ||
130       aSelectionType == TopAbs_SHELL || aSelectionType == TopAbs_WIRE)
131     {
132       Selector_Container* aContainer = new Selector_Container;
133       SET_ALGO_FLAGS(aContainer);
134       if (aContainer->select(theContext, theValue))
135         return aContainer;
136       delete aContainer;
137       return NULL;
138     }
139
140     Selector_Intersect* anIntersect = new Selector_Intersect;
141     SET_ALGO_FLAGS(anIntersect);
142     bool aGoodIntersector = anIntersect->select(theContext, theValue);
143     // weak intersector is bad for not use neighbors and has lower priority than neighbors
144     if (aGoodIntersector && anIntersect->myWeakIndex == -1) {
145       return anIntersect;
146     }
147     if (!theUseNeighbors) {
148       delete anIntersect;
149       return NULL;
150     }
151     // searching by neighbors
152     Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
153     SET_ALGO_FLAGS(aNBs);
154     // searching a context lab to store in NB algorithm
155     TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
156     if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
157       aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
158     if (aNBs->select(aContextLab, theContext, theValue)) {
159       delete anIntersect;
160       return aNBs;
161     }
162     delete aNBs;
163     if (aGoodIntersector) { // if neighbors are bad, use intersector with weak index
164       return anIntersect;
165     }
166     delete anIntersect;
167
168     // pure weak naming: there is no sense to use pure weak naming for neighbors selection
169     if (theUseNeighbors) {
170       Selector_WeakName* aWeak = new Selector_WeakName;
171       SET_ALGO_FLAGS(aWeak);
172       if (aWeak->select(theContext, theValue)) {
173         return aWeak;
174       }
175       delete aWeak;
176     }
177     return NULL; // not found value in the tree, not found context shape in the tree also
178   }
179   // getting label of this operation to avoid usage of ready shapes of this
180   TDF_Label aMyOpLab = theAccess;
181   if (!aMyOpLab.IsNull()) {
182     int aDepth = aMyOpLab.Depth();
183     if (aDepth < 3)
184       aMyOpLab.Nullify();
185     else {
186       for(; aDepth != 3; aDepth--)
187         aMyOpLab = aMyOpLab.Father();
188     }
189   }
190   // searching for the base shapes of the value
191   Handle(TNaming_NamedShape) aPrimitiveNS;
192   NCollection_List<Handle(TNaming_NamedShape)> aModifList;
193   for(int aUseExternal = 0; aUseExternal < 2; aUseExternal++) {
194     TDF_Label aLab = aUseExternal == 0 ? theAccess : theBaseDocument;
195     if (aLab.IsNull() || !TNaming_Tool::HasLabel(aLab, theValue))
196       continue;
197     for(TNaming_SameShapeIterator aShapes(theValue, aLab); aShapes.More(); aShapes.Next())
198     {
199       Handle(TNaming_NamedShape) aNS;
200       if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
201         TNaming_Evolution anEvolution = aNS->Evolution();
202         if (anEvolution == TNaming_SELECTED || anEvolution == TNaming_DELETE)
203           continue;
204         if (!aMyOpLab.IsNull() && aNS->Label().IsDescendant(aMyOpLab))
205           continue;
206         if (anEvolution == TNaming_PRIMITIVE) { // the value shape is declared as PRIMITIVE
207           aPrimitiveNS = aNS;
208           break;
209         } else if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
210           // check this is a new shape
211           TNaming_Iterator aNSIter(aNS);
212           for(; aNSIter.More(); aNSIter.Next())
213             if (aNSIter.NewShape().IsSame(theValue))
214               break;
215           if (aNSIter.More()) // new was found
216             aModifList.Append(aNS);
217         }
218       }
219     }
220   }
221
222   if (!aPrimitiveNS.IsNull()) {
223     Selector_Primitive* aPrimitive = new Selector_Primitive;
224     SET_ALGO_FLAGS(aPrimitive);
225     aPrimitive->select(aPrimitiveNS->Label());
226     return aPrimitive;
227   } else {
228     Selector_Modify* aModify = new Selector_Modify;
229     SET_ALGO_FLAGS(aModify);
230     bool aGoodModify = aModify->select(aModifList, theContext, theValue);
231     // weak intersector is bad for not use neighbors and has lower priority than neighbors
232     if (aGoodModify && aModify->myWeakIndex == -1) {
233       return aModify;
234     }
235     if (!theUseNeighbors) {
236       delete aModify;
237       return NULL;
238     }
239     // searching by neighbors
240     Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
241     SET_ALGO_FLAGS(aNBs);
242     // searching a context lab to store in NB algorithm
243     TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
244     if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
245       aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
246     if (aNBs->select(aContextLab, theContext, theValue)) {
247       delete aModify;
248       return aNBs;
249     }
250     delete aNBs;
251
252     if (aGoodModify) { // if neighbors are bad, use modify algo with weak index
253       return aModify;
254     }
255     delete aModify;
256   }
257
258   return NULL; // invalid case
259 }
260
261 Selector_Algo* Selector_Algo::relesectWithAllGeometry(
262   Selector_Algo* theOldAlgo, const TopoDS_Shape theContext)
263 {
264   return select(theContext, theOldAlgo->value(),
265     theOldAlgo->myLab, theOldAlgo->myBaseDocumentLab, true, true, true, true);
266 }
267
268 void Selector_Algo::storeBaseArray(const TDF_LabelList& theRef, const TDF_Label& theLast)
269 {
270   Handle(TDataStd_ReferenceArray) anArray =
271     TDataStd_ReferenceArray::Set(myLab, kBASE_ARRAY, 0, theRef.Extent());
272   Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
273   const TDF_Label aThisDocRoot = myLab.Root();
274   TDF_LabelList::Iterator aBIter(theRef);
275   for(int anIndex = 0; true; aBIter.Next(), anIndex++) {
276     const TDF_Label& aLab = aBIter.More() ? aBIter.Value() : theLast;
277     // check this is a label of this document
278     if (aLab.Root().IsEqual(aThisDocRoot)) {
279       anArray->SetValue(anIndex, aLab);
280     } else { // store reference to external document as an entry-string
281       if (anEntries.IsNull()) {
282         anEntries = TDataStd_ExtStringList::Set(myLab, kBASE_LIST);
283       }
284       TCollection_AsciiString anEntry;
285       TDF_Tool::Entry(aLab, anEntry);
286       anEntries->Append(anEntry);
287       anArray->SetValue(anIndex, aThisDocRoot); // stored root means it is external reference
288     }
289     if (!aBIter.More())
290       break;
291   }
292 }
293
294 bool Selector_Algo::restoreBaseArray(TDF_LabelList& theRef, TDF_Label& theLast)
295 {
296   const TDF_Label aThisDocRoot = myLab.Root();
297   Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
298   TDataStd_ListOfExtendedString::Iterator anIter;
299   Handle(TDataStd_ReferenceArray) anArray;
300   if (myLab.FindAttribute(kBASE_ARRAY, anArray)) {
301     int anUpper = anArray->Upper();
302     for(int anIndex = anArray->Lower(); anIndex <= anUpper; anIndex++) {
303       TDF_Label aLab = anArray->Value(anIndex);
304       if (aLab.IsEqual(aThisDocRoot)) { // external document reference
305         if (myBaseDocumentLab.IsNull())
306           return false;
307         if (anEntries.IsNull()) {
308           if (!myLab.FindAttribute(kBASE_LIST, anEntries))
309             return false;
310           anIter.Initialize(anEntries->List());
311         }
312         if (!anIter.More())
313           return false;
314         TDF_Tool::Label(myBaseDocumentLab.Data(), anIter.Value(), aLab);
315         anIter.Next();
316       }
317       if (anIndex == anUpper) {
318         theLast = aLab;
319       } else {
320         theRef.Append(aLab);
321       }
322     }
323     return true;
324   }
325   return false;
326 }
327
328 void Selector_Algo::store(const TopoDS_Shape theShape)
329 {
330   TNaming_Builder aBuilder(myLab);
331   aBuilder.Select(theShape, theShape);
332 }
333
334 bool Selector_Algo::sameGeometry(const TopoDS_Shape theShape1, const TopoDS_Shape theShape2) {
335   if (!theShape1.IsNull() && !theShape2.IsNull() && theShape1.ShapeType() == theShape2.ShapeType())
336   {
337     if (theShape1.ShapeType() == TopAbs_FACE) { // check surfaces
338       TopLoc_Location aLoc1, aLoc2;
339       TopoDS_Face aFace1 = TopoDS::Face(theShape1);
340       Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1, aLoc1);
341       TopoDS_Face aFace2 = TopoDS::Face(theShape2);
342       Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2, aLoc2);
343       return aSurf1 == aSurf2 && aLoc1.IsEqual(aLoc2);
344     } else if (theShape1.ShapeType() == TopAbs_EDGE) { // check curves
345       TopLoc_Location aLoc1, aLoc2;
346       Standard_Real aFirst, aLast;
347       TopoDS_Edge anEdge1 = TopoDS::Edge(theShape1);
348       Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aLoc1, aFirst, aLast);
349       TopoDS_Edge anEdge2 = TopoDS::Edge(theShape2);
350       Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aLoc2, aFirst, aLast);
351       return aCurve1 == aCurve2 && aLoc1.IsEqual(aLoc2);
352     }
353   }
354   return false;
355 }
356
357 TopoDS_Shape Selector_Algo::value()
358 {
359   Handle(TNaming_NamedShape) aNS;
360   if (myLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
361     return aNS->Get();
362   return TopoDS_Shape(); // empty, error shape
363 }
364
365 Selector_Algo* Selector_Algo::restoreByLab(TDF_Label theLab, TDF_Label theBaseDocLab)
366 {
367   Handle(TDataStd_Integer) aTypeAttr;
368   if (!theLab.FindAttribute(kSEL_TYPE, aTypeAttr))
369     return NULL;
370   Selector_Type aType = Selector_Type(aTypeAttr->Get());
371   Selector_Algo* aResult = NULL;
372   switch (aType) {
373   case SELTYPE_CONTAINER: {
374     aResult = new Selector_Container;
375     break;
376   }
377   case SELTYPE_INTERSECT: {
378     aResult = new Selector_Intersect;
379     break;
380   }
381   case SELTYPE_MODIFICATION: {
382     aResult = new Selector_Modify;
383     break;
384   }
385   case SELTYPE_PRIMITIVE: {
386     aResult = new Selector_Primitive;
387     break;
388   }
389   case SELTYPE_FILTER_BY_NEIGHBOR: {
390     aResult = new Selector_FilterByNeighbors;
391     break;
392   }
393   case SELTYPE_WEAK_NAMING: {
394     aResult = new Selector_WeakName;
395     break;
396   }
397   default: { // unknown case
398   }
399   }
400   if (aResult) {
401     aResult->myLab = theLab;
402     aResult->myBaseDocumentLab = theBaseDocLab;
403     aResult->myGeometricalNaming = theLab.IsAttribute(kGEOMETRICAL_NAMING);
404     if (!aResult->restore()) {
405       delete aResult;
406       aResult = NULL;
407     }
408   }
409   return aResult;
410 }
411
412 Selector_Algo* Selector_Algo::restoreByName(TDF_Label theLab, TDF_Label theBaseDocLab,
413   std::wstring theName, const TopAbs_ShapeEnum theShapeType, const bool theGeomNaming,
414   Selector_NameGenerator* theNameGenerator, TDF_Label& theContextLab)
415 {
416   Selector_Algo* aResult = NULL;
417   if (theName[0] == L'[') { // intersection or container
418     switch(theShapeType) {
419     case TopAbs_COMPOUND:
420     case TopAbs_COMPSOLID:
421     case TopAbs_SHELL:
422     case TopAbs_WIRE:
423       aResult = new Selector_Container;
424       break;
425     case TopAbs_VERTEX:
426     case TopAbs_EDGE:
427     case TopAbs_FACE:
428       aResult = new Selector_Intersect;
429       break;
430     default:;
431     }
432   } else if (theName[0] == L'(') { // filter by neighbors
433     aResult = new Selector_FilterByNeighbors;
434   } else if (theName.find(pureWeakNameID()) == 0 ||
435              theName.find(oldPureWeakNameID()) == 0) { // weak naming identifier
436     aResult = new Selector_WeakName;
437   } else if (theName.find(L'&') != std::wstring::npos) { // modification
438     aResult = new Selector_Modify;
439   } else { // primitive
440     aResult = new Selector_Primitive;
441   }
442   if (aResult) {
443     aResult->myLab = theLab;
444     aResult->myBaseDocumentLab = theBaseDocLab;
445     aResult->myGeometricalNaming = theGeomNaming;
446     theContextLab = aResult->restoreByName(theName, theShapeType, theNameGenerator);
447     if (theContextLab.IsNull()) {
448       delete aResult;
449       aResult = NULL;
450     }
451   }
452   return aResult;
453 }
454
455 void Selector_Algo::storeType(const Selector_Type theType)
456 {
457   myLab.ForgetAllAttributes(true);
458   TDataStd_Integer::Set(myLab, kSEL_TYPE, (int)(theType));
459   if (myGeometricalNaming)
460     TDataStd_UAttribute::Set(myLab, kGEOMETRICAL_NAMING);
461 }
462
463 /// Returns true if theSub is in theContext shape
464 static bool isInContext(const TopoDS_Shape& theContext, const TopoDS_Shape& theSub) {
465   for(TopExp_Explorer anExp(theContext, theSub.ShapeType()); anExp.More(); anExp.Next()) {
466     if (anExp.Current().IsSame(theSub))
467       return true;
468   }
469   return false;
470 }
471
472 bool Selector_Algo::findNewVersion(const TopoDS_Shape& theContext, TopoDS_Shape& theResult) const
473 {
474   if (theResult.IsNull())
475     return false;
476   if (!TNaming_Tool::HasLabel(myLab, theResult)) {
477     if (theResult.ShapeType() == TopAbs_COMPOUND) { // do it for all elements of compound
478       BRep_Builder aBuilder;
479       TopoDS_Compound aResultingCompound;
480       aBuilder.MakeCompound(aResultingCompound);
481       bool aWasChanged = false;
482       for (TopoDS_Iterator anIter(theResult); anIter.More(); anIter.Next()) {
483         TopoDS_Shape aSub = anIter.Value();
484         if (findNewVersion(theContext, aSub))
485           aWasChanged = true;
486         aBuilder.Add(aResultingCompound, aSub);
487       }
488       if (aWasChanged)
489         theResult = aResultingCompound;
490       return aWasChanged;
491     }
492   } else {
493     // check theResult is in theContext
494     if (isInContext(theContext, theResult))
495       return false;
496     // searching the next modifications of the result shape in document
497     TopTools_MapOfShape aResultShapes;
498     for(TNaming_NewShapeIterator aBaseIter(theResult, myLab); aBaseIter.More(); aBaseIter.Next())
499     {
500       TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
501       if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
502         TopoDS_Shape aNextModification = aBaseIter.Shape();
503         if (aNextModification.IsNull())
504           continue;
505         if (isInContext(theContext, aNextModification)) {
506           // don't add vertices generated from edges
507           if (aNextModification.ShapeType() <= theResult.ShapeType())
508             aResultShapes.Add(aNextModification);
509         }
510         else if (findNewVersion(theContext, aNextModification)) {
511           if (aNextModification.ShapeType() <= theResult.ShapeType())
512             aResultShapes.Add(aNextModification);
513         }
514       }
515     }
516     if (aResultShapes.IsEmpty())
517       return false;
518     if (aResultShapes.Size() == 1) {
519       theResult = TopTools_MapIteratorOfMapOfShape(aResultShapes).Value();
520     } else { // make a compound of all results
521       BRep_Builder aBuilder;
522       TopoDS_Compound aResultingCompound;
523       aBuilder.MakeCompound(aResultingCompound);
524       for(TopTools_MapIteratorOfMapOfShape anIter(aResultShapes); anIter.More(); anIter.Next())
525         aBuilder.Add(aResultingCompound, anIter.Value());
526       theResult = aResultingCompound;
527     }
528     return true;
529   }
530   return false;
531 }