1 // Copyright (C) 2014-2022 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 email : webmaster.salome@opencascade.com
20 #include <Selector_FilterByNeighbors.h>
22 #include <Selector_NameGenerator.h>
25 #include <TopoDS_Builder.hxx>
26 #include <TopoDS_Compound.hxx>
27 #include <TopTools_MapOfShape.hxx>
28 #include <TopExp_Explorer.hxx>
29 #include <TDataStd_Integer.hxx>
30 #include <TDataStd_IntegerArray.hxx>
31 #include <TDF_ChildIterator.hxx>
33 // array of the neighbor levels
34 static const Standard_GUID kLEVELS_ARRAY("ee4c4b45-e859-4e86-aa4f-6eac68e0ca1f");
36 Selector_FilterByNeighbors::Selector_FilterByNeighbors() : Selector_AlgoWithSubs()
39 /// Searches neighbor of theLevel of neighborhood to theValue in theContex
40 static void findNeighbors(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
41 const int theLevel, TopTools_MapOfShape& theResult)
43 TopAbs_ShapeEnum aConnectorType = TopAbs_VERTEX; // type of the connector sub-shapes
44 TopAbs_ShapeEnum aValueType = theValue.ShapeType();
45 if (aValueType == TopAbs_FACE)
46 aConnectorType = TopAbs_EDGE;
47 else if (aValueType == TopAbs_COMPOUND) { // for geometrical naming: compound of faces
48 TopExp_Explorer anExp(theValue, TopAbs_FACE);
50 aConnectorType = TopAbs_EDGE;
51 aValueType = TopAbs_FACE;
53 aValueType = TopAbs_EDGE;
56 TopTools_MapOfShape aNBConnectors; // connector shapes that already belong to neighbors
57 for(TopExp_Explorer aValExp(theValue, aConnectorType); aValExp.More(); aValExp.Next())
58 aNBConnectors.Add(aValExp.Current());
60 TopTools_MapOfShape alreadyProcessed;
61 if (aValueType == theValue.ShapeType())
62 alreadyProcessed.Add(theValue);
64 for(TopExp_Explorer aValExp(theValue, aValueType); aValExp.More(); aValExp.Next())
65 alreadyProcessed.Add(aValExp.Current());
67 for(int aLevel = 1; aLevel <= theLevel; aLevel++) {
68 TopoDS_ListOfShape aGoodCandidates;
69 TopExp_Explorer aCandidate(theContext, aValueType);
70 for(; aCandidate.More(); aCandidate.Next()) {
71 if (alreadyProcessed.Contains(aCandidate.Current()))
73 TopExp_Explorer aCandConnector(aCandidate.Current(), aConnectorType);
74 for(; aCandConnector.More(); aCandConnector.Next()) {
75 if (aNBConnectors.Contains(aCandConnector.Current())) // candidate is neighbor
78 if (aCandConnector.More()) {
79 if (aLevel == theLevel) { // add a NB into result: it is connected to other neighbors
80 theResult.Add(aCandidate.Current());
81 } else { // add to the NB of the current level
82 aGoodCandidates.Append(aCandidate.Current());
86 if (aLevel != theLevel) { // good candidates are added to neighbor of this level by connectors
87 for(TopoDS_ListOfShape::Iterator aGood(aGoodCandidates); aGood.More(); aGood.Next()) {
88 TopExp_Explorer aGoodConnector(aGood.Value(), aConnectorType);
89 for(; aGoodConnector.More(); aGoodConnector.Next()) {
90 aNBConnectors.Add(aGoodConnector.Current());
92 alreadyProcessed.Add(aGood.Value());
98 /// Searches the neighbor shape by neighbors defined in theNB in theContext shape
99 static const TopoDS_Shape findNeighbor(const TopoDS_Shape theContext,
100 const std::list<std::pair<TopoDS_Shape, int> >& theNB, const bool theGeometrical)
102 // searching for neighbors with minimum level
104 std::list<std::pair<TopoDS_Shape, int> >::const_iterator aNBIter = theNB.cbegin();
105 for(; aNBIter != theNB.cend(); aNBIter++) {
106 if (aMinLevel == 0 || aNBIter->second < aMinLevel) {
107 aMinLevel = aNBIter->second;
110 // collect all neighbors which are neighbors of sub-shapes with minimum level
112 TopoDS_ListOfShape aMatches;
113 for(aNBIter = theNB.cbegin(); aNBIter != theNB.cend(); aNBIter++) {
114 if (aNBIter->second == aMinLevel) {
115 TopTools_MapOfShape aThisNBs;
116 findNeighbors(theContext, aNBIter->first, aMinLevel, aThisNBs);
117 if (aFirst) { // aMatches must contain common part of all NBs lists
118 for(TopTools_MapOfShape::Iterator aThisNB(aThisNBs); aThisNB.More(); aThisNB.Next()) {
119 aMatches.Append(aThisNB.Value());
121 } else { // remove all in aMatches which are not in this NBs
122 for(TopoDS_ListOfShape::Iterator aMatch(aMatches); aMatch.More(); ) {
123 if (aThisNBs.Contains(aMatch.Value())) {
126 aMatches.Remove(aMatch);
133 if (aMatches.IsEmpty())
134 return TopoDS_Shape(); // not found any candidate
135 if (aMatches.Extent() == 1)
136 return aMatches.First(); // already found good candidate
137 TopoDS_Compound aResultCompound; // in case of geometrical name and many candidates
138 // iterate all matches to find by other (higher level) neighbors the best candidate
139 TopoDS_Shape aGoodCandidate;
140 TopTools_MapOfShape aGoodCandidates; // already added good candidates to the map
141 for(TopoDS_ListOfShape::Iterator aCandidate(aMatches); aCandidate.More(); aCandidate.Next()) {
142 bool aValidCadidate = true;
143 for(int aLevel = aMinLevel + 1; true; aLevel++) {
144 bool aFooundHigherLevel = false;
145 TopoDS_ListOfShape aLevelNBs;
146 for(aNBIter = theNB.cbegin(); aNBIter != theNB.cend(); aNBIter++) {
147 if (aNBIter->second == aLevel)
148 aLevelNBs.Append(aNBIter->first);
149 else if (aNBIter->second >= aLevel)
150 aFooundHigherLevel = true;
152 if (!aFooundHigherLevel && aLevelNBs.IsEmpty()) { // iterated all, so, good candidate
153 if (aGoodCandidate.IsNull()) {
154 aGoodCandidate = aCandidate.Value();
155 } else { // another good candidate
156 if (theGeometrical && Selector_Algo::sameGeometry(aGoodCandidate, aCandidate.Value())) {
157 if (!aGoodCandidates.Add(aCandidate.Value()))
159 static TopoDS_Builder aBuilder;
160 if (aResultCompound.IsNull()) {
161 aBuilder.MakeCompound(aResultCompound);
162 aBuilder.Add(aResultCompound, aGoodCandidate);
164 aBuilder.Add(aResultCompound, aCandidate.Value());
166 return TopoDS_Shape();
168 break; // no more NBs with higher levels
170 if (!aLevelNBs.IsEmpty()) {
171 TopTools_MapOfShape aNBsOfCandidate;
172 findNeighbors(theContext, aCandidate.Value(), aLevel, aNBsOfCandidate);
173 // check all stored neighbors are in the map of real neighbors
174 for(TopoDS_ListOfShape::Iterator aLevIter(aLevelNBs); aLevIter.More(); aLevIter.Next()) {
175 if (!aNBsOfCandidate.Contains(aLevIter.Value())) {
176 aValidCadidate = false;
181 if (!aValidCadidate) // candidate is not valid, break the checking
185 if (!aResultCompound.IsNull())
186 return aResultCompound;
187 return aGoodCandidate;
190 bool Selector_FilterByNeighbors::select(
191 const TDF_Label theContextLab, const TopoDS_Shape theContext, const TopoDS_Shape theValue)
193 myShapeType = theValue.ShapeType();
194 myContext = theContextLab;
195 // searching by neighbors
196 std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
197 // # 19071 : moving through all levels may be too long, in practice there are no more than 2 or 3
198 // levels real cases, so, make it "5" as maximum possible
199 for(int aLevel = 1; aLevel < 5; aLevel++) {
200 TopTools_MapOfShape aNewNB;
201 findNeighbors(theContext, theValue, aLevel, aNewNB);
202 if (aNewNB.Extent() == 0) { // there are no neighbors of the given level, stop iteration
205 // iterate by the order in theContext to keep same naming names
206 TopExp_Explorer anOrder(theContext, theValue.ShapeType());
207 TopTools_MapOfShape alreadyProcessed;
208 for (; anOrder.More(); anOrder.Next()) {
209 if (alreadyProcessed.Add(anOrder.Current()) && aNewNB.Contains(anOrder.Current())) {
210 TopoDS_Shape aNewNBShape = anOrder.Current();
211 // check which can be named correctly, without "by neighbors" type
212 Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNewNBShape,
213 newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
215 // add to list of good NBs
216 aNBs.push_back(std::pair<TopoDS_Shape, int>(aNewNBShape, aLevel));
218 delete aSubAlgo; // don't keep this sub-algo until all subs and whole validity are checked
221 TopoDS_Shape aResult = findNeighbor(theContext, aNBs, geometricalNaming());
222 if (!aResult.IsNull() && aResult.IsSame(theValue)) {
223 std::list<std::pair<TopoDS_Shape, int> >::iterator aNBIter = aNBs.begin();
224 for(; aNBIter != aNBs.end(); aNBIter++) {
225 Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, aNBIter->first,
226 newSubLabel(), baseDocument(), geometricalNaming(), false, useIntersections());
227 if (append(aSubAlgo)) {
228 myNBLevel.push_back(aNBIter->second);
239 void Selector_FilterByNeighbors::store()
241 storeType(Selector_Algo::SELTYPE_FILTER_BY_NEIGHBOR);
242 TDataStd_Integer::Set(label(), shapeTypeID(), (int)myShapeType);
243 // store numbers of levels corresponded to the neighbors in sub-selectors
244 Handle(TDataStd_IntegerArray) anArray =
245 TDataStd_IntegerArray::Set(label(), kLEVELS_ARRAY, 0, int(myNBLevel.size()) - 1);
246 std::list<int>::iterator aLevel = myNBLevel.begin();
247 for(int anIndex = 0; aLevel != myNBLevel.end(); aLevel++, anIndex++) {
248 anArray->SetValue(anIndex, Abs(*aLevel));
250 // store all sub-selectors
251 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
252 for(; aSubSel != list().cend(); aSubSel++) {
255 // store context reference if exists
256 if (!myContext.IsNull()) {
257 static const TDF_LabelList anEmptyRefList;
258 storeBaseArray(anEmptyRefList, myContext);
262 bool Selector_FilterByNeighbors::restore()
264 Handle(TDataStd_Integer) aShapeTypeAttr;
265 if (!label().FindAttribute(shapeTypeID(), aShapeTypeAttr))
267 myShapeType = TopAbs_ShapeEnum(aShapeTypeAttr->Get());
268 // restore levels indices
269 Handle(TDataStd_IntegerArray) anArray;
270 if (!label().FindAttribute(kLEVELS_ARRAY, anArray))
272 for(int anIndex = 0; anIndex <= anArray->Upper(); anIndex++) {
273 myNBLevel.push_back(anArray->Value(anIndex));
275 // restore sub-selectors
276 for(TDF_ChildIterator aSub(label(), false); aSub.More(); aSub.Next()) {
277 Selector_Algo* aSubSel = restoreByLab(aSub.Value(), baseDocument());
278 if (!append(aSubSel, false)) {
279 if (!aSub.Value().HasAttribute())
280 break; // some empty label left in the end
281 // some selector fails, try to use rest selectors, myNBLevel becomes negative: unused
282 if (myNBLevel.size() > list().size()) {
283 std::list<int>::iterator aListIter = myNBLevel.begin();
284 for(size_t a = 0; a < list().size(); a++)
286 *aListIter = -*aListIter;
287 list().push_back(NULL);
291 // restore context reference if exists
292 static TDF_LabelList anEmptyRefList;
293 restoreBaseArray(anEmptyRefList, myContext);
295 return myNBLevel.size() == list().size() && !myNBLevel.empty();
298 TDF_Label Selector_FilterByNeighbors::restoreByName(std::wstring theName,
299 const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
301 myShapeType = theShapeType;
303 std::wstring aLastLevel; // last level string (after '(' ) to check the context name in the end
304 for (size_t aStart = 0; aStart != std::wstring::npos;
305 aStart = theName.find(L'(', aStart + 1)) {
306 size_t anEndPos = theName.find(L')', aStart + 1);
307 if (anEndPos != std::wstring::npos) {
308 std::wstring aSubStr = theName.substr(aStart + 1, anEndPos - aStart - 1);
309 TDF_Label aSubContext;
310 Selector_Algo* aSubSel =
311 Selector_Algo::restoreByName(newSubLabel(), baseDocument(), aSubStr, myShapeType,
312 geometricalNaming(), theNameGenerator, aSubContext);
313 if (!append(aSubSel))
316 if (aSubContext.IsNull()) {
321 if (!aContext.IsNull() && !aContext.IsEqual(aSubContext)) {
322 if (!theNameGenerator->isLater(aContext, aSubContext))
323 aContext = aSubContext;
325 aContext = aSubContext;
327 //if (!aContext.IsNull()) // for filters by neighbor the latest context shape is vital
328 // aContext = theNameGenerator->newestContext(aContext);
330 // searching for the level index
332 for (anEndPos++; anEndPos != std::wstring::npos &&
333 theName[anEndPos] != L'(' && theName[anEndPos] != 0;
335 aLevel += theName[anEndPos];
338 if (aLevel.empty() || aLevel[0] == L'_')
339 myNBLevel.push_back(1); // by default it is 1
341 int aNum = std::stoi(aLevel.c_str());
343 myNBLevel.push_back(aNum);
345 return TDF_Label(); // invalid number
348 return TDF_Label(); // invalid parentheses
350 if (!aLastLevel.empty()) { // get the context
351 size_t aLinePos = aLastLevel.find(L"_");
352 if (aLinePos != std::wstring::npos) {
353 std::wstring aContextName = aLastLevel.substr(aLinePos + 1);
354 if (!aContextName.empty()) {
355 TDF_Label aThisContext, aValue;
356 if (theNameGenerator->restoreContext(aContextName, aThisContext, aValue)) {
357 if (!aThisContext.IsNull())
358 aContext = aThisContext;
363 myContext = aContext;
367 bool Selector_FilterByNeighbors::solve(const TopoDS_Shape& theContext)
369 TopoDS_Shape aResult;
371 std::list<std::pair<TopoDS_Shape, int> > aNBs; /// neighbor sub-shape -> level of neighborhood
372 std::list<int>::iterator aLevel = myNBLevel.begin();
373 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
374 for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
376 continue; // skip because sub-selector is not good
377 if ((*aSubSel)->solve(theContext)) {
378 aNBs.push_back(std::pair<TopoDS_Shape, int>((*aSubSel)->value(), *aLevel));
382 aResult = findNeighbor(theContext, aNBs, geometricalNaming());
383 if (!aResult.IsNull()) {
384 Selector_Algo::store(aResult);
391 std::wstring Selector_FilterByNeighbors::name(Selector_NameGenerator* theNameGenerator)
393 // (nb1)level_if_more_than_1(nb2)level_if_more_than_1(nb3)level_if_more_than_1
394 bool aThisContextNameNeeded = !myContext.IsNull();
395 std::wstring aContextName;
396 if (aThisContextNameNeeded)
397 aContextName = theNameGenerator->contextName(myContext);
398 std::wstring aResult;
399 std::list<int>::iterator aLevel = myNBLevel.begin();
400 std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
401 for(; aSubSel != list().cend(); aSubSel++, aLevel++) {
404 std::wstring aSubName = (*aSubSel)->name(theNameGenerator);
405 if (aSubName.empty())
407 aResult += L"(" + aSubName + L")";
409 std::wostringstream aLevelStr;
411 aResult += aLevelStr.str();
413 // sub-name already contains the needed context name, so, here it is not needed
414 if (aThisContextNameNeeded && (
415 aSubName.find(aContextName) == 0 || aSubName.substr(1).find(aContextName) == 0))
416 aThisContextNameNeeded = false;
418 if (aThisContextNameNeeded) {
419 aResult = aResult + L"_" + aContextName;