Salome HOME
Merge remote-tracking branch 'remotes/origin/HigherLevelObjectsHistory'
[modules/shaper.git] / src / Selector / Selector_Algo.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
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   if (TNaming_Tool::HasLabel(theAccess, theShape)) { // selection and delete evolution are not used
72     for(TNaming_SameShapeIterator aShapes(theShape, theAccess); aShapes.More(); aShapes.Next())
73     {
74       Handle(TNaming_NamedShape) aNS;
75       if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
76         if (aNS->Evolution() == TNaming_MODIFY || aNS->Evolution() == TNaming_GENERATED ||
77             aNS->Evolution() == TNaming_PRIMITIVE) {
78           aResult = aNS->Label();
79           break;
80         }
81       }
82     }
83   }
84   return aResult;
85 }
86
87 #define SET_ALGO_FLAGS(algo) \
88   algo->myLab = theAccess; \
89   algo->myBaseDocumentLab = theBaseDocument; \
90   algo->myGeometricalNaming = theGeometricalNaming; \
91   algo->myUseNeighbors = theUseNeighbors; \
92   algo->myUseIntersections = theUseIntersections; \
93   algo->myAlwaysGeometricalNaming = theAlwaysGeometricalNaming;
94
95 Selector_Algo* Selector_Algo::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
96   const TDF_Label theAccess, const TDF_Label theBaseDocument,
97   const bool theGeometricalNaming, const bool theUseNeighbors, const bool theUseIntersections,
98   const bool theAlwaysGeometricalNaming)
99 {
100   Selector_Algo* aResult = NULL;
101   if (theValue.IsNull() || theContext.IsNull())
102     return aResult;
103
104   // check the value shape can be named as it is, or it is needed to construct it from the
105   // higher level shapes (like a box vertex by faces that form this vertex)
106   bool aIsFound = !findGoodLabelWithShape(theAccess, theValue).IsNull();
107   if (!aIsFound && !theBaseDocument.IsNull()) // searching in the base document
108     aIsFound = !findGoodLabelWithShape(theBaseDocument, theValue).IsNull();
109   if (!aIsFound) {
110     TopAbs_ShapeEnum aSelectionType = theValue.ShapeType();
111     if (aSelectionType == TopAbs_COMPOUND || aSelectionType == TopAbs_COMPSOLID ||
112       aSelectionType == TopAbs_SHELL || aSelectionType == TopAbs_WIRE)
113     {
114       Selector_Container* aContainer = new Selector_Container;
115       SET_ALGO_FLAGS(aContainer);
116       if (aContainer->select(theContext, theValue))
117         return aContainer;
118       delete aContainer;
119       return NULL;
120     }
121
122     Selector_Intersect* anIntersect = new Selector_Intersect;
123     SET_ALGO_FLAGS(anIntersect);
124     bool aGoodIntersector = anIntersect->select(theContext, theValue);
125     // weak intersector is bad for not use neighbors and has lower priority than neighbors
126     if (aGoodIntersector && anIntersect->myWeakIndex == -1) {
127       return anIntersect;
128     }
129     if (!theUseNeighbors) {
130       delete anIntersect;
131       return NULL;
132     }
133     // searching by neighbors
134     Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
135     SET_ALGO_FLAGS(aNBs);
136     // searching a context lab to store in NB algorithm
137     TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
138     if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
139       aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
140     if (aNBs->select(aContextLab, theContext, theValue)) {
141       delete anIntersect;
142       return aNBs;
143     }
144     delete aNBs;
145     if (aGoodIntersector) { // if neighbors are bad, use intersector with weak index
146       return anIntersect;
147     }
148     delete anIntersect;
149
150     // pure weak naming: there is no sense to use pure weak naming for neighbors selection
151     if (theUseNeighbors) {
152       Selector_WeakName* aWeak = new Selector_WeakName;
153       SET_ALGO_FLAGS(aWeak);
154       if (aWeak->select(theContext, theValue)) {
155         return aWeak;
156       }
157       delete aWeak;
158     }
159     return NULL; // not found value in the tree, not found context shape in the tree also
160   }
161   // searching for the base shapes of the value
162   Handle(TNaming_NamedShape) aPrimitiveNS;
163   NCollection_List<Handle(TNaming_NamedShape)> aModifList;
164   for(int aUseExternal = 0; aUseExternal < 2; aUseExternal++) {
165     TDF_Label aLab = aUseExternal == 0 ? theAccess : theBaseDocument;
166     if (aLab.IsNull() || !TNaming_Tool::HasLabel(aLab, theValue))
167       continue;
168     for(TNaming_SameShapeIterator aShapes(theValue, aLab); aShapes.More(); aShapes.Next())
169     {
170       Handle(TNaming_NamedShape) aNS;
171       if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
172         TNaming_Evolution anEvolution = aNS->Evolution();
173         if (anEvolution == TNaming_PRIMITIVE) { // the value shape is declared as PRIMITIVE
174           aPrimitiveNS = aNS;
175           break;
176         } else if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
177           // check this is a new shape
178           TNaming_Iterator aNSIter(aNS);
179           for(; aNSIter.More(); aNSIter.Next())
180             if (aNSIter.NewShape().IsSame(theValue))
181               break;
182           if (aNSIter.More()) // new was found
183             aModifList.Append(aNS);
184         }
185       }
186     }
187   }
188
189   if (!aPrimitiveNS.IsNull()) {
190     Selector_Primitive* aPrimitive = new Selector_Primitive;
191     SET_ALGO_FLAGS(aPrimitive);
192     aPrimitive->select(aPrimitiveNS->Label());
193     return aPrimitive;
194   } else {
195     Selector_Modify* aModify = new Selector_Modify;
196     SET_ALGO_FLAGS(aModify);
197     bool aGoodModify = aModify->select(aModifList, theContext, theValue);
198     // weak intersector is bad for not use neighbors and has lower priority than neighbors
199     if (aGoodModify && aModify->myWeakIndex == -1) {
200       return aModify;
201     }
202     if (!theUseNeighbors) {
203       delete aModify;
204       return NULL;
205     }
206     // searching by neighbors
207     Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
208     SET_ALGO_FLAGS(aNBs);
209     // searching a context lab to store in NB algorithm
210     TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
211     if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
212       aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
213     if (aNBs->select(aContextLab, theContext, theValue)) {
214       delete aModify;
215       return aNBs;
216     }
217     delete aNBs;
218
219     if (aGoodModify) { // if neighbors are bad, use modify algo with weak index
220       return aModify;
221     }
222     delete aModify;
223   }
224
225   return NULL; // invalid case
226 }
227
228 Selector_Algo* Selector_Algo::relesectWithAllGeometry(
229   Selector_Algo* theOldAlgo, const TopoDS_Shape theContext)
230 {
231   return select(theContext, theOldAlgo->value(),
232     theOldAlgo->myLab, theOldAlgo->myBaseDocumentLab, true, true, true, true);
233 }
234
235 void Selector_Algo::storeBaseArray(const TDF_LabelList& theRef, const TDF_Label& theLast)
236 {
237   Handle(TDataStd_ReferenceArray) anArray =
238     TDataStd_ReferenceArray::Set(myLab, kBASE_ARRAY, 0, theRef.Extent());
239   Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
240   const TDF_Label aThisDocRoot = myLab.Root();
241   TDF_LabelList::Iterator aBIter(theRef);
242   for(int anIndex = 0; true; aBIter.Next(), anIndex++) {
243     const TDF_Label& aLab = aBIter.More() ? aBIter.Value() : theLast;
244     // check this is a label of this document
245     if (aLab.Root().IsEqual(aThisDocRoot)) {
246       anArray->SetValue(anIndex, aLab);
247     } else { // store reference to external document as an entry-string
248       if (anEntries.IsNull()) {
249         anEntries = TDataStd_ExtStringList::Set(myLab, kBASE_LIST);
250       }
251       TCollection_AsciiString anEntry;
252       TDF_Tool::Entry(aLab, anEntry);
253       anEntries->Append(anEntry);
254       anArray->SetValue(anIndex, aThisDocRoot); // stored root means it is external reference
255     }
256     if (!aBIter.More())
257       break;
258   }
259 }
260
261 bool Selector_Algo::restoreBaseArray(TDF_LabelList& theRef, TDF_Label& theLast)
262 {
263   const TDF_Label aThisDocRoot = myLab.Root();
264   Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
265   TDataStd_ListOfExtendedString::Iterator anIter;
266   Handle(TDataStd_ReferenceArray) anArray;
267   if (myLab.FindAttribute(kBASE_ARRAY, anArray)) {
268     int anUpper = anArray->Upper();
269     for(int anIndex = anArray->Lower(); anIndex <= anUpper; anIndex++) {
270       TDF_Label aLab = anArray->Value(anIndex);
271       if (aLab.IsEqual(aThisDocRoot)) { // external document reference
272         if (myBaseDocumentLab.IsNull())
273           return false;
274         if (anEntries.IsNull()) {
275           if (!myLab.FindAttribute(kBASE_LIST, anEntries))
276             return false;
277           anIter.Initialize(anEntries->List());
278         }
279         if (!anIter.More())
280           return false;
281         TDF_Tool::Label(myBaseDocumentLab.Data(), anIter.Value(), aLab);
282         anIter.Next();
283       }
284       if (anIndex == anUpper) {
285         theLast = aLab;
286       } else {
287         theRef.Append(aLab);
288       }
289     }
290     return true;
291   }
292   return false;
293 }
294
295 void Selector_Algo::store(const TopoDS_Shape theShape)
296 {
297   TNaming_Builder aBuilder(myLab);
298   aBuilder.Select(theShape, theShape);
299 }
300
301 bool Selector_Algo::sameGeometry(const TopoDS_Shape theShape1, const TopoDS_Shape theShape2) {
302   if (!theShape1.IsNull() && !theShape2.IsNull() && theShape1.ShapeType() == theShape2.ShapeType())
303   {
304     if (theShape1.ShapeType() == TopAbs_FACE) { // check surfaces
305       TopLoc_Location aLoc1, aLoc2;
306       TopoDS_Face aFace1 = TopoDS::Face(theShape1);
307       Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1, aLoc1);
308       TopoDS_Face aFace2 = TopoDS::Face(theShape2);
309       Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2, aLoc2);
310       return aSurf1 == aSurf2 && aLoc1.IsEqual(aLoc2);
311     } else if (theShape1.ShapeType() == TopAbs_EDGE) { // check curves
312       TopLoc_Location aLoc1, aLoc2;
313       Standard_Real aFirst, aLast;
314       TopoDS_Edge anEdge1 = TopoDS::Edge(theShape1);
315       Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aLoc1, aFirst, aLast);
316       TopoDS_Edge anEdge2 = TopoDS::Edge(theShape2);
317       Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aLoc2, aFirst, aLast);
318       return aCurve1 == aCurve2 && aLoc1.IsEqual(aLoc2);
319     }
320   }
321   return false;
322 }
323
324 TopoDS_Shape Selector_Algo::value()
325 {
326   Handle(TNaming_NamedShape) aNS;
327   if (myLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
328     return aNS->Get();
329   return TopoDS_Shape(); // empty, error shape
330 }
331
332 Selector_Algo* Selector_Algo::restoreByLab(TDF_Label theLab, TDF_Label theBaseDocLab)
333 {
334   Handle(TDataStd_Integer) aTypeAttr;
335   if (!theLab.FindAttribute(kSEL_TYPE, aTypeAttr))
336     return NULL;
337   Selector_Type aType = Selector_Type(aTypeAttr->Get());
338   Selector_Algo* aResult = NULL;
339   switch (aType) {
340   case SELTYPE_CONTAINER: {
341     aResult = new Selector_Container;
342     break;
343   }
344   case SELTYPE_INTERSECT: {
345     aResult = new Selector_Intersect;
346     break;
347   }
348   case SELTYPE_MODIFICATION: {
349     aResult = new Selector_Modify;
350     break;
351   }
352   case SELTYPE_PRIMITIVE: {
353     aResult = new Selector_Primitive;
354     break;
355   }
356   case SELTYPE_FILTER_BY_NEIGHBOR: {
357     aResult = new Selector_FilterByNeighbors;
358     break;
359   }
360   case SELTYPE_WEAK_NAMING: {
361     aResult = new Selector_WeakName;
362     break;
363   }
364   default: { // unknown case
365   }
366   }
367   if (aResult) {
368     aResult->myLab = theLab;
369     aResult->myBaseDocumentLab = theBaseDocLab;
370     aResult->myGeometricalNaming = theLab.IsAttribute(kGEOMETRICAL_NAMING);
371     if (!aResult->restore()) {
372       delete aResult;
373       aResult = NULL;
374     }
375   }
376   return aResult;
377 }
378
379 Selector_Algo* Selector_Algo::restoreByName(TDF_Label theLab, TDF_Label theBaseDocLab,
380   std::string theName, const TopAbs_ShapeEnum theShapeType, const bool theGeomNaming,
381   Selector_NameGenerator* theNameGenerator, TDF_Label& theContextLab)
382 {
383   Selector_Algo* aResult = NULL;
384   if (theName[0] == '[') { // intersection or container
385     switch(theShapeType) {
386     case TopAbs_COMPOUND:
387     case TopAbs_COMPSOLID:
388     case TopAbs_SHELL:
389     case TopAbs_WIRE:
390       aResult = new Selector_Container;
391       break;
392     case TopAbs_VERTEX:
393     case TopAbs_EDGE:
394     case TopAbs_FACE:
395       aResult = new Selector_Intersect;
396       break;
397     default:;
398     }
399   } else if (theName[0] == '(') { // filter by neighbors
400     aResult = new Selector_FilterByNeighbors;
401   } else if (theName.find(pureWeakNameID()) == 0) { // weak naming identifier
402     aResult = new Selector_WeakName;
403   } else if (theName.find('&') != std::string::npos) { // modification
404     aResult = new Selector_Modify;
405   } else { // primitive
406     aResult = new Selector_Primitive;
407   }
408   if (aResult) {
409     aResult->myLab = theLab;
410     aResult->myBaseDocumentLab = theBaseDocLab;
411     aResult->myGeometricalNaming = theGeomNaming;
412     theContextLab = aResult->restoreByName(theName, theShapeType, theNameGenerator);
413     if (theContextLab.IsNull()) {
414       delete aResult;
415       aResult = NULL;
416     }
417   }
418   return aResult;
419 }
420
421 void Selector_Algo::storeType(const Selector_Type theType)
422 {
423   myLab.ForgetAllAttributes(true);
424   TDataStd_Integer::Set(myLab, kSEL_TYPE, (int)(theType));
425   if (myGeometricalNaming)
426     TDataStd_UAttribute::Set(myLab, kGEOMETRICAL_NAMING);
427 }
428
429 /// Returns true if theSub is in theContext shape
430 static bool isInContext(const TopoDS_Shape& theContext, const TopoDS_Shape& theSub) {
431   for(TopExp_Explorer anExp(theContext, theSub.ShapeType()); anExp.More(); anExp.Next()) {
432     if (anExp.Current().IsSame(theSub))
433       return true;
434   }
435   return false;
436 }
437
438 bool Selector_Algo::findNewVersion(const TopoDS_Shape& theContext, TopoDS_Shape& theResult) const
439 {
440   if (theResult.IsNull())
441     return false;
442   if (!TNaming_Tool::HasLabel(myLab, theResult)) {
443     if (theResult.ShapeType() == TopAbs_COMPOUND) { // do it for all elements of compound
444       BRep_Builder aBuilder;
445       TopoDS_Compound aResultingCompound;
446       aBuilder.MakeCompound(aResultingCompound);
447       bool aWasChanged = false;
448       for (TopoDS_Iterator anIter(theResult); anIter.More(); anIter.Next()) {
449         TopoDS_Shape aSub = anIter.Value();
450         if (findNewVersion(theContext, aSub))
451           aWasChanged = true;
452         aBuilder.Add(aResultingCompound, aSub);
453       }
454       if (aWasChanged)
455         theResult = aResultingCompound;
456       return aWasChanged;
457     }
458   } else {
459     // check theResult is in theContext
460     if (isInContext(theContext, theResult))
461       return false;
462     // searching the next modifications of the result shape in document
463     TopTools_MapOfShape aResultShapes;
464     for(TNaming_NewShapeIterator aBaseIter(theResult, myLab); aBaseIter.More(); aBaseIter.Next())
465     {
466       TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
467       if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
468         TopoDS_Shape aNextModification = aBaseIter.Shape();
469         if (aNextModification.IsNull())
470           continue;
471         if (isInContext(theContext, aNextModification))
472           // don't add vertices generated from edges
473           if (aNextModification.ShapeType() <= theResult.ShapeType())
474             aResultShapes.Add(aNextModification);
475         else if (findNewVersion(theContext, aNextModification))
476           if (aNextModification.ShapeType() <= theResult.ShapeType())
477             aResultShapes.Add(aNextModification);
478       }
479     }
480     if (aResultShapes.IsEmpty())
481       return false;
482     if (aResultShapes.Size() == 1) {
483       theResult = TopTools_MapIteratorOfMapOfShape(aResultShapes).Value();
484     } else { // make a compound of all results
485       BRep_Builder aBuilder;
486       TopoDS_Compound aResultingCompound;
487       aBuilder.MakeCompound(aResultingCompound);
488       for(TopTools_MapIteratorOfMapOfShape anIter(aResultShapes); anIter.More(); anIter.Next())
489         aBuilder.Add(aResultingCompound, anIter.Value());
490       theResult = aResultingCompound;
491     }
492     return true;
493   }
494   return false;
495 }