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