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