1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include <Selector_FilterByNeighbors.h>
23 #include <Selector_NameGenerator.h>
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>
34 // array of the neighbor levels
35 static const Standard_GUID kLEVELS_ARRAY("ee4c4b45-e859-4e86-aa4f-6eac68e0ca1f");
37 Selector_FilterByNeighbors::Selector_FilterByNeighbors() : Selector_AlgoWithSubs()
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)
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());
52 TopTools_MapOfShape alreadyProcessed;
53 alreadyProcessed.Add(theValue);
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()))
61 TopExp_Explorer aCandConnector(aCandidate.Current(), aConnectorType);
62 for(; aCandConnector.More(); aCandConnector.Next()) {
63 if (aNBConnectors.Contains(aCandConnector.Current())) // candidate is neighbor
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());
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());
80 alreadyProcessed.Add(aGood.Value());
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)
90 // searching for neighbors with minimum level
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;
98 // collect all neighbors which are neighbors of sub-shapes with minimum level
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()) {
108 aMatches.Append(aThisNB.Value());
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())) {
115 aMatches.Remove(aMatch);
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;
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()))
149 static TopoDS_Builder aBuilder;
150 if (aResultCompound.IsNull()) {
151 aBuilder.MakeCompound(aResultCompound);
152 aBuilder.Add(aResultCompound, aGoodCandidate);
154 aBuilder.Add(aResultCompound, aCandidate.Value());
156 return TopoDS_Shape();
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;
170 if (!aValidCadidate) // candidate is not valid, break the checking
174 if (!aResultCompound.IsNull())
175 return aResultCompound;
176 return aGoodCandidate;
179 bool Selector_FilterByNeighbors::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue)
181 myShapeType = theValue.ShapeType();
182 // searching by neighbors
183 std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
184 for(int aLevel = 1; true; aLevel++) {
185 TopTools_MapOfShape aNewNB;
186 findNeighbors(theContext, theValue, aLevel, aNewNB);
187 if (aNewNB.Extent() == 0) { // there are no neighbors of the given level, stop iteration
190 // iterate by the order in theContext to keep same naming names
191 TopExp_Explorer anOrder(theContext, theValue.ShapeType());
192 TopTools_MapOfShape alreadyProcessed;
193 for (; anOrder.More(); anOrder.Next()) {
194 if (alreadyProcessed.Add(anOrder.Current()) && aNewNB.Contains(anOrder.Current())) {
195 TopoDS_Shape aNewNBShape = anOrder.Current();
196 // check which can be named correctly, without "by neighbors" type
197 Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNewNBShape,
198 newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
200 // add to list of good NBs
201 aNBs.push_back(std::pair<TopoDS_Shape, int>(aNewNBShape, aLevel));
203 delete aSubAlgo; // don't keep this sub-algo until all subs and whole validity are checked
206 TopoDS_Shape aResult = findNeighbor(theContext, aNBs, geometricalNaming());
207 if (!aResult.IsNull() && aResult.IsSame(theValue)) {
208 std::list<std::pair<TopoDS_Shape, int> >::iterator aNBIter = aNBs.begin();
209 for(; aNBIter != aNBs.end(); aNBIter++) {
210 Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNBIter->first,
211 newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
212 if (append(aSubAlgo)) {
213 myNBLevel.push_back(aNBIter->second);
224 void Selector_FilterByNeighbors::store()
226 storeType(Selector_Algo::SELTYPE_FILTER_BY_NEIGHBOR);
227 TDataStd_Integer::Set(label(), shapeTypeID(), (int)myShapeType);
228 // store numbers of levels corresponded to the neighbors in sub-selectors
229 Handle(TDataStd_IntegerArray) anArray =
230 TDataStd_IntegerArray::Set(label(), kLEVELS_ARRAY, 0, int(myNBLevel.size()) - 1);
231 std::list<int>::iterator aLevel = myNBLevel.begin();
232 for(int anIndex = 0; aLevel != myNBLevel.end(); aLevel++, anIndex++) {
233 anArray->SetValue(anIndex, Abs(*aLevel));
235 // store all sub-selectors
236 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
237 for(; aSubSel != list().cend(); aSubSel++) {
242 bool Selector_FilterByNeighbors::restore()
244 Handle(TDataStd_Integer) aShapeTypeAttr;
245 if (!label().FindAttribute(shapeTypeID(), aShapeTypeAttr))
247 myShapeType = TopAbs_ShapeEnum(aShapeTypeAttr->Get());
248 // restore levels indices
249 Handle(TDataStd_IntegerArray) anArray;
250 if (!label().FindAttribute(kLEVELS_ARRAY, anArray))
252 for(int anIndex = 0; anIndex <= anArray->Upper(); anIndex++) {
253 myNBLevel.push_back(anArray->Value(anIndex));
255 // restore sub-selectors
256 bool aSubResult = true;
257 for(TDF_ChildIterator aSub(label(), false); aSub.More(); aSub.Next()) {
258 Selector_Algo* aSubSel = restoreByLab(aSub.Value(), baseDocument());
259 if (!append(aSubSel, false)) {
260 if (!aSub.Value().HasAttribute())
261 break; // some empty label left in the end
262 // some selector fails, try to use rest selectors, myNBLevel becomes negative: unused
263 if (myNBLevel.size() > list().size()) {
264 std::list<int>::iterator aListIter = myNBLevel.begin();
265 for(int a = 0; a < list().size(); a++)
267 *aListIter = -*aListIter;
268 list().push_back(NULL);
272 return myNBLevel.size() == list().size() && !myNBLevel.empty();
275 TDF_Label Selector_FilterByNeighbors::restoreByName(std::string theName,
276 const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
278 myShapeType = theShapeType;
280 for (size_t aStart = 0; aStart != std::string::npos;
281 aStart = theName.find('(', aStart + 1)) {
282 size_t anEndPos = theName.find(')', aStart + 1);
283 if (anEndPos != std::string::npos) {
284 std::string aSubStr = theName.substr(aStart + 1, anEndPos - aStart - 1);
285 TDF_Label aSubContext;
286 Selector_Algo* aSubSel =
287 Selector_Algo::restoreByName(newSubLabel(), baseDocument(), aSubStr, myShapeType,
288 geometricalNaming(), theNameGenerator, aSubContext);
289 if (!append(aSubSel))
292 if (aSubContext.IsNull()) {
297 if (!aContext.IsNull() && !aContext.IsEqual(aSubContext)) {
298 if (!theNameGenerator->isLater(aContext, aSubContext))
299 aContext = aSubContext;
301 aContext = aSubContext;
303 if (!aContext.IsNull()) // for filters by neighbor the latest context shape is vital
304 aContext = theNameGenerator->newestContext(aContext);
306 // searching for the level index
308 for (anEndPos++; anEndPos != std::string::npos &&
309 theName[anEndPos] != '(' && theName[anEndPos] != 0;
311 aLevel += theName[anEndPos];
314 myNBLevel.push_back(1); // by default it is 1
316 int aNum = atoi(aLevel.c_str());
318 myNBLevel.push_back(aNum);
320 return TDF_Label(); // invalid number
323 return TDF_Label(); // invalid parentheses
328 bool Selector_FilterByNeighbors::solve(const TopoDS_Shape& theContext)
330 TopoDS_Shape aResult;
332 std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
333 std::list<int>::iterator aLevel = myNBLevel.begin();
334 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
335 for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
337 continue; // skip because sub-selector is not good
338 if ((*aSubSel)->solve(theContext)) {
339 aNBs.push_back(std::pair<TopoDS_Shape, int>((*aSubSel)->value(), *aLevel));
343 aResult = findNeighbor(theContext, aNBs, geometricalNaming());
344 if (!aResult.IsNull()) {
345 Selector_Algo::store(aResult);
352 std::string Selector_FilterByNeighbors::name(Selector_NameGenerator* theNameGenerator)
354 // (nb1)level_if_more_than_1(nb2)level_if_more_than_1(nb3)level_if_more_than_1
356 std::list<int>::iterator aLevel = myNBLevel.begin();
357 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
358 for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
361 aResult += "(" + (*aSubSel)->name(theNameGenerator) + ")";
363 std::ostringstream aLevelStr;
365 aResult += aLevelStr.str();