]> SALOME platform Git repositories - modules/shaper.git/blob - src/Selector/Selector_FilterByNeighbors.cpp
Salome HOME
Fix for the robinet model with partitions: final groups selection with neighbors...
[modules/shaper.git] / src / Selector / Selector_FilterByNeighbors.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_FilterByNeighbors.h>
22
23 #include <Selector_NameGenerator.h>
24
25 #include <TopoDS.hxx>
26 #include <TopoDS_Builder.hxx>
27 #include <TopoDS_Compound.hxx>
28 #include <TopTools_MapOfShape.hxx>
29 #include <TopExp_Explorer.hxx>
30 #include <TDataStd_Integer.hxx>
31 #include <TDataStd_IntegerArray.hxx>
32 #include <TDF_ChildIterator.hxx>
33
34 // array of the neighbor levels
35 static const Standard_GUID kLEVELS_ARRAY("ee4c4b45-e859-4e86-aa4f-6eac68e0ca1f");
36
37 Selector_FilterByNeighbors::Selector_FilterByNeighbors() : Selector_AlgoWithSubs()
38 {}
39
40 /// Searches neighbor of theLevel of neighborhood to theValue in theContex
41 static void findNeighbors(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
42   const int theLevel, TopTools_MapOfShape& theResult)
43 {
44   TopAbs_ShapeEnum aConnectorType = TopAbs_VERTEX; // type of the connector sub-shapes
45   if (theValue.ShapeType() == TopAbs_FACE)
46     aConnectorType = TopAbs_EDGE;
47   TopTools_MapOfShape aNBConnectors; // connector shapes that already belong to neighbors
48   for(TopExp_Explorer aValExp(theValue, aConnectorType); aValExp.More(); aValExp.Next()) {
49     aNBConnectors.Add(aValExp.Current());
50   }
51
52   TopTools_MapOfShape alreadyProcessed;
53   alreadyProcessed.Add(theValue);
54
55   for(int aLevel = 1; aLevel <= theLevel; aLevel++) {
56     TopoDS_ListOfShape aGoodCandidates;
57     TopExp_Explorer aCandidate(theContext, theValue.ShapeType());
58     for(; aCandidate.More(); aCandidate.Next()) {
59       if (alreadyProcessed.Contains(aCandidate.Current()))
60         continue;
61       TopExp_Explorer aCandConnector(aCandidate.Current(), aConnectorType);
62       for(; aCandConnector.More(); aCandConnector.Next()) {
63         if (aNBConnectors.Contains(aCandConnector.Current())) // candidate is neighbor
64           break;
65       }
66       if (aCandConnector.More()) {
67         if (aLevel == theLevel) { // add a NB into result: it is connected to other neighbors
68           theResult.Add(aCandidate.Current());
69         } else { // add to the NB of the current level
70           aGoodCandidates.Append(aCandidate.Current());
71         }
72       }
73     }
74     if (aLevel != theLevel) { // good candidates are added to neighbor of this level by connectors
75       for(TopoDS_ListOfShape::Iterator aGood(aGoodCandidates); aGood.More(); aGood.Next()) {
76         TopExp_Explorer aGoodConnector(aGood.Value(), aConnectorType);
77         for(; aGoodConnector.More(); aGoodConnector.Next()) {
78           aNBConnectors.Add(aGoodConnector.Current());
79         }
80         alreadyProcessed.Add(aGood.Value());
81       }
82     }
83   }
84 }
85
86 /// Searches the neighbor shape by neighbors defined in theNB in theContext shape
87 static const TopoDS_Shape findNeighbor(const TopoDS_Shape theContext,
88   const std::list<std::pair<TopoDS_Shape, int> >& theNB, const bool theGeometrical)
89 {
90   // searching for neighbors with minimum level
91   int aMinLevel = 0;
92   std::list<std::pair<TopoDS_Shape, int> >::const_iterator aNBIter = theNB.cbegin();
93   for(; aNBIter != theNB.cend(); aNBIter++) {
94     if (aMinLevel == 0 || aNBIter->second < aMinLevel) {
95       aMinLevel = aNBIter->second;
96     }
97   }
98   // collect all neighbors which are neighbors of sub-shapes with minimum level
99   bool aFirst = true;
100   TopoDS_ListOfShape aMatches;
101   for(aNBIter = theNB.cbegin(); aNBIter != theNB.cend(); aNBIter++) {
102     if (aNBIter->second == aMinLevel) {
103       TopTools_MapOfShape aThisNBs;
104       findNeighbors(theContext, aNBIter->first, aMinLevel, aThisNBs);
105       // aMatches must contain common part of all NBs lists
106       for(TopTools_MapOfShape::Iterator aThisNB(aThisNBs); aThisNB.More(); aThisNB.Next()) {
107         if (aFirst) {
108           aMatches.Append(aThisNB.Value());
109         } else {
110           // remove all in aMatches which are not in this NBs
111           for(TopoDS_ListOfShape::Iterator aMatch(aMatches); aMatch.More(); ) {
112             if (aThisNBs.Contains(aMatch.Value())) {
113               aMatch.Next();
114             } else {
115               aMatches.Remove(aMatch);
116             }
117           }
118         }
119       }
120       aFirst = false;
121     }
122   }
123   if (aMatches.IsEmpty())
124     return TopoDS_Shape(); // not found any candidate
125   if (aMatches.Extent() == 1)
126     return aMatches.First(); // already found good candidate
127   TopoDS_Compound aResultCompound; // in case of geometrical name and many candidates
128   // iterate all matches to find by other (higher level) neighbors the best candidate
129   TopoDS_Shape aGoodCandidate;
130   TopTools_MapOfShape aGoodCandidates; // already added good candidates to the map
131   for(TopoDS_ListOfShape::Iterator aCandidate(aMatches); aCandidate.More(); aCandidate.Next()) {
132     bool aValidCadidate = true;
133     for(int aLevel = aMinLevel + 1; true; aLevel++) {
134       bool aFooundHigherLevel = false;
135       TopoDS_ListOfShape aLevelNBs;
136       for(aNBIter = theNB.cbegin(); aNBIter != theNB.cend(); aNBIter++) {
137         if (aNBIter->second == aLevel)
138           aLevelNBs.Append(aNBIter->first);
139         else if (aNBIter->second >= aLevel)
140           aFooundHigherLevel = true;
141       }
142       if (!aFooundHigherLevel && aLevelNBs.IsEmpty()) { // iterated all, so, good candidate
143         if (aGoodCandidate.IsNull()) {
144           aGoodCandidate = aCandidate.Value();
145         } else { // another good candidate
146           if (theGeometrical && Selector_Algo::sameGeometry(aGoodCandidate, aCandidate.Value())) {
147             if (!aGoodCandidates.Add(aCandidate.Value()))
148               break;
149             static TopoDS_Builder aBuilder;
150             if (aResultCompound.IsNull()) {
151               aBuilder.MakeCompound(aResultCompound);
152               aBuilder.Add(aResultCompound, aGoodCandidate);
153             }
154             aBuilder.Add(aResultCompound, aCandidate.Value());
155           } else
156             return TopoDS_Shape();
157         }
158       }
159       if (!aLevelNBs.IsEmpty()) {
160         TopTools_MapOfShape aNBsOfCandidate;
161         findNeighbors(theContext, aCandidate.Value(), aLevel, aNBsOfCandidate);
162         // check all stored neighbors are in the map of real neighbors
163         for(TopoDS_ListOfShape::Iterator aLevIter(aLevelNBs); aLevIter.More(); aLevIter.Next()) {
164           if (!aNBsOfCandidate.Contains(aLevIter.Value())) {
165             aValidCadidate = false;
166             break;
167           }
168         }
169       }
170       if (!aValidCadidate) // candidate is not valid, break the checking
171         break;
172     }
173   }
174   if (!aResultCompound.IsNull())
175     return aResultCompound;
176   return aGoodCandidate;
177 }
178
179 bool Selector_FilterByNeighbors::select(
180   const TDF_Label theContextLab, const TopoDS_Shape theContext, const TopoDS_Shape theValue)
181 {
182   myShapeType = theValue.ShapeType();
183   myContext = theContextLab;
184   // searching by neighbors
185   std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
186   for(int aLevel = 1; true; aLevel++) {
187     TopTools_MapOfShape aNewNB;
188     findNeighbors(theContext, theValue, aLevel, aNewNB);
189     if (aNewNB.Extent() == 0) { // there are no neighbors of the given level, stop iteration
190       break;
191     }
192     // iterate by the order in theContext to keep same naming names
193     TopExp_Explorer anOrder(theContext, theValue.ShapeType());
194     TopTools_MapOfShape alreadyProcessed;
195     for (; anOrder.More(); anOrder.Next()) {
196       if (alreadyProcessed.Add(anOrder.Current()) && aNewNB.Contains(anOrder.Current())) {
197         TopoDS_Shape aNewNBShape = anOrder.Current();
198         // check which can be named correctly, without "by neighbors" type
199         Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNewNBShape,
200           newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
201         if (aSubAlgo) {
202           // add to list of good NBs
203           aNBs.push_back(std::pair<TopoDS_Shape, int>(aNewNBShape, aLevel));
204         }
205         delete aSubAlgo; // don't keep this sub-algo until all subs and whole validity are checked
206       }
207     }
208     TopoDS_Shape aResult = findNeighbor(theContext, aNBs, geometricalNaming());
209     if (!aResult.IsNull() && aResult.IsSame(theValue)) {
210       std::list<std::pair<TopoDS_Shape, int> >::iterator aNBIter = aNBs.begin();
211       for(; aNBIter != aNBs.end(); aNBIter++) {
212         Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNBIter->first,
213           newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
214         if (append(aSubAlgo)) {
215           myNBLevel.push_back(aNBIter->second);
216         } else {
217           delete aSubAlgo;
218         }
219       }
220       return true;
221     }
222   }
223   return false;
224 }
225
226 void Selector_FilterByNeighbors::store()
227 {
228   storeType(Selector_Algo::SELTYPE_FILTER_BY_NEIGHBOR);
229   TDataStd_Integer::Set(label(), shapeTypeID(), (int)myShapeType);
230   // store numbers of levels corresponded to the neighbors in sub-selectors
231   Handle(TDataStd_IntegerArray) anArray =
232     TDataStd_IntegerArray::Set(label(), kLEVELS_ARRAY, 0, int(myNBLevel.size()) - 1);
233   std::list<int>::iterator aLevel = myNBLevel.begin();
234   for(int anIndex = 0; aLevel != myNBLevel.end(); aLevel++, anIndex++) {
235     anArray->SetValue(anIndex, Abs(*aLevel));
236   }
237   // store all sub-selectors
238   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
239   for(; aSubSel != list().cend(); aSubSel++) {
240     (*aSubSel)->store();
241   }
242   // store context reference if exists
243   if (!myContext.IsNull()) {
244     static const TDF_LabelList anEmptyRefList;
245     storeBaseArray(anEmptyRefList, myContext);
246   }
247 }
248
249 bool Selector_FilterByNeighbors::restore()
250 {
251   Handle(TDataStd_Integer) aShapeTypeAttr;
252   if (!label().FindAttribute(shapeTypeID(), aShapeTypeAttr))
253     return false;
254   myShapeType = TopAbs_ShapeEnum(aShapeTypeAttr->Get());
255   // restore levels indices
256   Handle(TDataStd_IntegerArray) anArray;
257   if (!label().FindAttribute(kLEVELS_ARRAY, anArray))
258     return false;
259   for(int anIndex = 0; anIndex <= anArray->Upper(); anIndex++) {
260     myNBLevel.push_back(anArray->Value(anIndex));
261   }
262   // restore sub-selectors
263   bool aSubResult = true;
264   for(TDF_ChildIterator aSub(label(), false); aSub.More(); aSub.Next()) {
265     Selector_Algo* aSubSel = restoreByLab(aSub.Value(), baseDocument());
266     if (!append(aSubSel, false)) {
267       if (!aSub.Value().HasAttribute())
268         break; // some empty label left in the end
269       // some selector fails, try to use rest selectors, myNBLevel becomes negative: unused
270       if (myNBLevel.size() > list().size()) {
271         std::list<int>::iterator aListIter = myNBLevel.begin();
272         for(int a = 0; a < list().size(); a++)
273           aListIter++;
274         *aListIter = -*aListIter;
275         list().push_back(NULL);
276       }
277     }
278   }
279   // restore context reference if exists
280   static TDF_LabelList anEmptyRefList;
281   restoreBaseArray(anEmptyRefList, myContext);
282
283   return myNBLevel.size() == list().size() && !myNBLevel.empty();
284 }
285
286 TDF_Label Selector_FilterByNeighbors::restoreByName(std::string theName,
287   const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
288 {
289   myShapeType = theShapeType;
290   TDF_Label aContext;
291   std::string aLastLevel; // last level string (after '(' )  to check the context name in the end
292   for (size_t aStart = 0; aStart != std::string::npos;
293     aStart = theName.find('(', aStart + 1)) {
294     size_t anEndPos = theName.find(')', aStart + 1);
295     if (anEndPos != std::string::npos) {
296       std::string aSubStr = theName.substr(aStart + 1, anEndPos - aStart - 1);
297       TDF_Label aSubContext;
298       Selector_Algo* aSubSel =
299         Selector_Algo::restoreByName(newSubLabel(), baseDocument(), aSubStr, myShapeType,
300           geometricalNaming(), theNameGenerator, aSubContext);
301       if (!append(aSubSel))
302         return TDF_Label();
303
304       if (aSubContext.IsNull()) {
305         delete aSubSel;
306         clearSubAlgos();
307         return TDF_Label();
308       }
309       if (!aContext.IsNull() && !aContext.IsEqual(aSubContext)) {
310         if (!theNameGenerator->isLater(aContext, aSubContext))
311           aContext = aSubContext;
312       } else {
313         aContext = aSubContext;
314       }
315       if (!aContext.IsNull()) // for filters by neighbor the latest context shape is vital
316         aContext = theNameGenerator->newestContext(aContext);
317
318       // searching for the level index
319       std::string aLevel;
320       for (anEndPos++; anEndPos != std::string::npos &&
321         theName[anEndPos] != '(' && theName[anEndPos] != 0;
322         anEndPos++) {
323         aLevel += theName[anEndPos];
324       }
325       aLastLevel = aLevel;
326       if (aLevel.empty() || aLevel[0] == '_')
327         myNBLevel.push_back(1); // by default it is 1
328       else {
329         int aNum = atoi(aLevel.c_str());
330         if (aNum > 0)
331           myNBLevel.push_back(aNum);
332         else
333           return TDF_Label(); // invalid number
334       }
335     } else
336       return TDF_Label(); // invalid parentheses
337   }
338   if (!aLastLevel.empty()) { // get the context
339     size_t aLinePos = aLastLevel.find("_");
340     if (aLinePos != std::string::npos) {
341       std::string aContextName = aLastLevel.substr(aLinePos + 1);
342       if (!aContextName.empty()) {
343         TDF_Label aThisContext, aValue;
344         if (theNameGenerator->restoreContext(aContextName, aThisContext, aValue)) {
345           if (!aThisContext.IsNull())
346             aContext = aThisContext;
347         }
348       }
349     }
350   }
351   myContext = aContext;
352   return aContext;
353 }
354
355 bool Selector_FilterByNeighbors::solve(const TopoDS_Shape& theContext)
356 {
357   TopoDS_Shape aResult;
358
359   std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
360   std::list<int>::iterator aLevel = myNBLevel.begin();
361   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
362   for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
363     if (*aLevel < 0)
364       continue; // skip because sub-selector is not good
365     if ((*aSubSel)->solve(theContext)) {
366       aNBs.push_back(std::pair<TopoDS_Shape, int>((*aSubSel)->value(), *aLevel));
367     }
368   }
369   if (!aNBs.empty()) {
370     aResult = findNeighbor(theContext, aNBs, geometricalNaming());
371     if (!aResult.IsNull()) {
372       Selector_Algo::store(aResult);
373       return true;
374     }
375   }
376   return false;
377 }
378
379 std::string Selector_FilterByNeighbors::name(Selector_NameGenerator* theNameGenerator)
380 {
381   // (nb1)level_if_more_than_1(nb2)level_if_more_than_1(nb3)level_if_more_than_1
382   bool aThisContextNameNeeded = !myContext.IsNull();
383   std::string aContextName;
384   if (aThisContextNameNeeded)
385     aContextName = theNameGenerator->contextName(myContext);
386   std::string aResult;
387   std::list<int>::iterator aLevel = myNBLevel.begin();
388   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
389   for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
390     if (!*aSubSel)
391       continue;
392     std::string aSubName = (*aSubSel)->name(theNameGenerator);
393     aResult += "(" + aSubName + ")";
394     if (*aLevel > 1) {
395       std::ostringstream aLevelStr;
396       aLevelStr<<*aLevel;
397       aResult += aLevelStr.str();
398     }
399     // sub-name already contains the needed context name, so, here it is not needed
400     if (aThisContextNameNeeded && (
401          aSubName.find(aContextName) == 0 || aSubName.substr(1).find(aContextName)) == 0)
402       aThisContextNameNeeded = false;
403   }
404   if (aThisContextNameNeeded) {
405     aResult = aResult + "_" + aContextName;
406   }
407   return aResult;
408 }