Salome HOME
Issue #19058: Error in sketch projection when changing parameter
[modules/shaper.git] / src / Selector / Selector_Intersect.cpp
1 // Copyright (C) 2014-2020  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 email : webmaster.salome@opencascade.com
18 //
19
20 #include <Selector_Intersect.h>
21
22 #include <Selector_NameGenerator.h>
23 #include <Selector_NExplode.h>
24
25 #include <TNaming_NamedShape.hxx>
26 #include <TDataStd_Name.hxx>
27 #include <TDataStd_Integer.hxx>
28 #include <TDF_ChildIterator.hxx>
29 #include <TopTools_MapOfShape.hxx>
30 #include <TopExp_Explorer.hxx>
31 #include <BRep_Tool.hxx>
32 #include <TopoDS.hxx>
33 #include <TopoDS_Builder.hxx>
34 #include <TopoDS_Compound.hxx>
35
36 Selector_Intersect::Selector_Intersect() : Selector_AlgoWithSubs()
37 {
38   myWeakIndex = -1; // no index by default
39   myRecomputeWeakIndex = false;
40 }
41
42 // returns the sub-shapes of theSubType which belong to all theShapes (so, common or intersection)
43 static void commonShapes(const TopoDS_ListOfShape& theShapes, TopAbs_ShapeEnum theSubType,
44   TopoDS_ListOfShape& theResults)
45 {
46   TopoDS_ListOfShape::iterator aSubSel = theShapes.begin();
47   for(; aSubSel != theShapes.end(); aSubSel++) {
48     TopTools_MapOfShape aCurrentMap;
49     for(TopExp_Explorer anExp(*aSubSel, theSubType); anExp.More(); anExp.Next()) {
50       if (aCurrentMap.Add(anExp.Current()) && aSubSel == theShapes.begin())
51         theResults.Append(anExp.Current());
52     }
53     if (aSubSel != theShapes.begin()) { // remove from common shapes not in aCurrentMap
54       for(TopoDS_ListOfShape::Iterator aComIter(theResults); aComIter.More(); ) {
55         if (aCurrentMap.Contains(aComIter.Value()))
56           aComIter.Next();
57         else
58           theResults.Remove(aComIter);
59       }
60     }
61   }
62 }
63
64 bool Selector_Intersect::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue)
65 {
66   if (!useIntersections())
67     return false;
68   myShapeType = theValue.ShapeType();
69   TopAbs_ShapeEnum aSelectionType = myShapeType;
70   // try to find the shape of the higher level type in the context shape
71   bool aFacesTried = false; // for identification of vertices, faces are tried, then edges
72   TopoDS_ListOfShape aLastCommon; // store not good commons, but which may be used for weak naming
73   TopoDS_ListOfShape aLastIntersectors;
74   while(aSelectionType != TopAbs_FACE || !aFacesTried) {
75     if (aSelectionType == TopAbs_FACE) {
76       if (theValue.ShapeType() != TopAbs_VERTEX)
77         break;
78       aFacesTried = true;
79       aSelectionType = TopAbs_EDGE;
80     } else
81       aSelectionType = TopAbs_FACE;
82     TopTools_MapOfShape anIntersectors; // shapes of aSelectionType that contain theValue
83     TopoDS_ListOfShape anIntList; // same as anIntersectors
84     for(TopExp_Explorer aSelExp(theContext, aSelectionType); aSelExp.More(); aSelExp.Next()) {
85       if (aSelectionType == TopAbs_EDGE &&
86         BRep_Tool::Degenerated(TopoDS::Edge(aSelExp.Current())))
87         continue;
88       TopExp_Explorer aSubExp(aSelExp.Current(), theValue.ShapeType());
89       for(; aSubExp.More(); aSubExp.Next()) {
90         if (aSubExp.Current().IsSame(theValue)) {
91           if (anIntersectors.Add(aSelExp.Current()))
92             anIntList.Append(aSelExp.Current());
93           break;
94         }
95       }
96     }
97     // check that solution is only one
98     TopoDS_ListOfShape aCommon;
99     commonShapes(anIntList, theValue.ShapeType(), aCommon);
100     if (aCommon.Extent() == 1 && aCommon.First().IsSame(theValue)) {
101       // name the intersectors
102       TopoDS_ListOfShape::Iterator anInt(anIntList);
103       for (; anInt.More(); anInt.Next()) {
104         Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, anInt.Value(),
105           newSubLabel(), baseDocument(), geometricalNaming(), useNeighbors(), false);
106         if (!append(aSubAlgo))
107           break; // if some selector is failed, stop and search another solution
108       }
109       if (!anInt.More()) { // all intersectors were correctly named
110         return true;
111       }
112     } else if (aCommon.Extent() > 1 && aLastCommon.IsEmpty())  {
113       aLastCommon = aCommon;
114       aLastIntersectors = anIntList;
115     }
116   }
117   if (aLastCommon.Extent() > 1) {
118     if (alwaysGeometricalNaming()) {
119       TopoDS_ListOfShape::Iterator aCommonIter(aLastCommon);
120       TopoDS_Shape aFirst = aCommonIter.Value();
121       for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
122         if (!sameGeometry(aFirst, aCommonIter.Value()))
123           break;
124       }
125       if (!aCommonIter.More()) { // all geometry is same, result is a compound
126         return true;
127       }
128     }
129     // weak naming to distinguish commons coming from intersection
130     Selector_NExplode aNexp(aLastCommon);
131     myWeakIndex = aNexp.index(theValue);
132     if (myWeakIndex != -1) {
133       // name the intersectors
134       TopoDS_ListOfShape::Iterator anInt(aLastIntersectors);
135       for (; anInt.More(); anInt.Next()) {
136         Selector_Algo* aSubAlgo = Selector_Algo::select(theContext, anInt.Value(),
137           newSubLabel(), baseDocument(), geometricalNaming(), useNeighbors(), false);
138         if (!append(aSubAlgo))
139           break; // if some selector is failed, stop and search another solution
140       }
141       if (!anInt.More()) { // all intersectors were correctly named
142         return true;
143       }
144     }
145   }
146   return false; // solution does not found
147 }
148
149 void Selector_Intersect::store()
150 {
151   storeType(Selector_Algo::SELTYPE_INTERSECT);
152   // store all sub-selectors
153   TDataStd_Integer::Set(label(), shapeTypeID(), (int)myShapeType);
154   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
155   for(; aSubSel != list().cend(); aSubSel++) {
156     (*aSubSel)->store();
157   }
158   TDataStd_Integer::Set(label(), shapeTypeID(), (int)myShapeType);
159   if (myWeakIndex != -1) {
160     TDataStd_Integer::Set(label(), weakID(), myWeakIndex);
161   }
162 }
163
164 bool Selector_Intersect::restore()
165 {
166   Handle(TDataStd_Integer) aShapeTypeAttr;
167   if (!label().FindAttribute(shapeTypeID(), aShapeTypeAttr))
168     return false;
169   myShapeType = TopAbs_ShapeEnum(aShapeTypeAttr->Get());
170   // restore sub-selectors
171   bool aSubResult = true;
172   for(TDF_ChildIterator aSub(label(), false); aSub.More(); aSub.Next()) {
173     Selector_Algo* aSubSel = restoreByLab(aSub.Value(), baseDocument());
174     if (!append(aSubSel, false)) {
175       break; // some empty label left in the end
176     }
177   }
178   Handle(TDataStd_Integer) aWeakInt;
179   if (label().FindAttribute(weakID(), aWeakInt)) {
180     myWeakIndex = aWeakInt->Get();
181   }
182   return true;
183 }
184
185 TDF_Label Selector_Intersect::restoreByName(std::string theName,
186   const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
187 {
188   myShapeType = theShapeType;
189   TDF_Label aContext;
190   for(size_t aStart = 0; aStart != std::string::npos; aStart = theName.find('[', aStart + 1)) {
191     size_t anEndPos = theName.find(']', aStart + 1);
192     if (anEndPos != std::string::npos) {
193       std::string aSubStr = theName.substr(aStart + 1, anEndPos - aStart - 1);
194       size_t aFoundOldWeak = aSubStr.find(oldWeakNameID());
195       size_t aFoundNewWeak = aFoundOldWeak != std::string::npos ?
196                              aSubStr.find(weakNameID()) :
197                              aFoundOldWeak;
198       if (aFoundOldWeak == 0 || aFoundNewWeak == 0) { // weak name identifier
199         std::string aWeakIndex = aSubStr.substr(aFoundOldWeak + oldWeakNameID().size());
200         myWeakIndex = atoi(aWeakIndex.c_str());
201         myRecomputeWeakIndex = aFoundOldWeak == 0;
202         continue;
203       }
204       TopAbs_ShapeEnum aSubShapeType = TopAbs_FACE;
205       if (anEndPos != std::string::npos && anEndPos + 1 < theName.size()) {
206         char aShapeChar = theName[anEndPos + 1];
207         if (theName[anEndPos + 1] != '[') {
208           switch(aShapeChar) {
209           case 'e': aSubShapeType = TopAbs_EDGE; break;
210           case 'v': aSubShapeType = TopAbs_VERTEX; break;
211           default:;
212           }
213         }
214       }
215       TDF_Label aSubContext;
216       Selector_Algo* aSubSel =
217         Selector_Algo::restoreByName(newSubLabel(), baseDocument(), aSubStr, aSubShapeType,
218           geometricalNaming(), theNameGenerator, aSubContext);
219       if (!append(aSubSel))
220         return TDF_Label();
221
222       if (aSubContext.IsNull()) {
223         delete aSubSel;
224         clearSubAlgos();
225         return TDF_Label();
226       }
227       if (!aContext.IsNull() && !aContext.IsEqual(aSubContext)) {
228         if (!theNameGenerator->isLater(aContext, aSubContext))
229           aContext = aSubContext;
230       } else {
231         aContext = aSubContext;
232       }
233     } else
234       return TDF_Label(); // invalid parentheses
235   }
236   return aContext;
237 }
238
239 bool Selector_Intersect::solve(const TopoDS_Shape& theContext)
240 {
241   TopoDS_Shape aResult;
242   TopoDS_ListOfShape aSubSelectorShapes;
243   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
244   for(; aSubSel != list().cend(); aSubSel++) {
245     if (!(*aSubSel)->solve(theContext)) {
246       return false;
247     }
248     aSubSelectorShapes.Append((*aSubSel)->value());
249   }
250   TopoDS_ListOfShape aCommon; // common sub shapes in each sub-selector (a result)
251   commonShapes(aSubSelectorShapes, myShapeType, aCommon);
252   if (aCommon.Extent() != 1) {
253     if (myWeakIndex != -1) {
254       Selector_NExplode aNexp(aCommon, myRecomputeWeakIndex);
255       aResult = aNexp.shape(myWeakIndex);
256       myRecomputeWeakIndex = false;
257     } else if (geometricalNaming() && aCommon.Extent() > 1) {
258       // check results are on the same geometry, create compound
259       TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
260       TopoDS_Shape aFirst = aCommonIter.Value();
261       for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
262         if (!sameGeometry(aFirst, aCommonIter.Value()))
263           break;
264       }
265       if (!aCommonIter.More()) { // all geometry is same, create a result compound
266         TopoDS_Builder aBuilder;
267         TopoDS_Compound aCompound;
268         aBuilder.MakeCompound(aCompound);
269         for(aCommonIter.Initialize(aCommon); aCommonIter.More(); aCommonIter.Next()) {
270           aBuilder.Add(aCompound, aCommonIter.Value());
271         }
272         aResult = aCompound;
273       }
274     } else {
275       return false;
276     }
277   } else {
278     aResult = aCommon.First();
279   }
280   if (!aResult.IsNull()) {
281     Selector_Algo::store(aResult);
282     return true;
283   }
284   return false;
285 }
286
287 std::string Selector_Intersect::name(Selector_NameGenerator* theNameGenerator)
288 {
289   std::string aResult;
290   // add names of sub-components one by one in "[]" +optionally [weak_name_1]
291   std::list<Selector_Algo*>::const_iterator aSubSel = list().cbegin();
292   for(; aSubSel != list().cend(); aSubSel++) {
293     aResult += '[';
294     aResult += (*aSubSel)->name(theNameGenerator);
295     aResult += ']';
296     TopoDS_Shape aSubVal = (*aSubSel)->value();
297     if (!aSubVal.IsNull()) {
298       TopAbs_ShapeEnum aSubType = aSubVal.ShapeType();
299       if (aSubType != TopAbs_FACE) { // in case the sub shape type must be stored
300         switch(aSubType) {
301         case TopAbs_EDGE: aResult += "e"; break;
302         case TopAbs_VERTEX: aResult += "v"; break;
303         default:;
304         }
305       }
306     }
307   }
308   if (myWeakIndex != -1) {
309     std::ostringstream aWeakStr;
310     aWeakStr<<"["<<weakNameID()<<myWeakIndex<<"]";
311     aResult += aWeakStr.str();
312   }
313   return aResult;
314 }