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