Salome HOME
98d1337191e88ce4dde95ff2dac5c91493f067fc
[modules/shaper.git] / src / Selector / Selector_Modify.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_Modify.h>
21
22 #include <Selector_NameGenerator.h>
23 #include <Selector_NExplode.h>
24
25 #include <Locale_Convert.h>
26
27 #include <TNaming_NamedShape.hxx>
28 #include <TNaming_Iterator.hxx>
29 #include <TNaming_SameShapeIterator.hxx>
30 #include <TNaming_NewShapeIterator.hxx>
31 #include <TNaming_Tool.hxx>
32 #include <TDataStd_Name.hxx>
33 #include <TDataStd_Integer.hxx>
34 #include <TDF_ChildIterator.hxx>
35 #include <TopTools_MapOfShape.hxx>
36 #include <TopExp_Explorer.hxx>
37 #include <BRep_Tool.hxx>
38 #include <TopoDS.hxx>
39 #include <TopoDS_Builder.hxx>
40 #include <TopoDS_Compound.hxx>
41
42 Selector_Modify::Selector_Modify() : Selector_Algo()
43 {
44   myWeakIndex = -1; // no index by default
45   myRecomputeWeakIndex = false;
46 }
47
48 // adds to theResult all labels that contain initial shapes for theValue located in theFinal
49 static void findBases(TDF_Label theAccess, Handle(TNaming_NamedShape) theFinal,
50   const TopoDS_Shape& theValue,
51   bool aMustBeAtFinal, const TDF_Label& theAdditionalDocument, TDF_LabelList& theResult)
52 {
53   bool aFoundAnyShape = false;
54   if (TNaming_Tool::HasLabel(theAccess, theValue)) {
55     TNaming_SameShapeIterator aLabIter(theValue, theAccess);
56     for(; aLabIter.More(); aLabIter.Next()) {
57       Handle(TNaming_NamedShape) aNS;
58       if (aLabIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
59         if (aMustBeAtFinal && aNS != theFinal)
60           continue; // looking for old at the same final label only
61         TNaming_Evolution anEvolution = aNS->Evolution();
62         if (anEvolution == TNaming_PRIMITIVE) {
63           // check that this is not in the results already
64           const TDF_Label aResult = aNS->Label();
65           TDF_LabelList::Iterator aResIter(theResult);
66           for(; aResIter.More(); aResIter.Next()) {
67             if (aResIter.Value().IsEqual(aResult))
68               break;
69           }
70           if (!aResIter.More()) // not found, so add this new
71             theResult.Append(aResult);
72           aFoundAnyShape = true;
73         }
74         if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
75           for(TNaming_Iterator aThisIter(aNS); aThisIter.More(); aThisIter.Next()) {
76             if (aThisIter.NewShape().IsSame(theValue)) {
77               // continue recursively, null NS means that any NS are ok
78               findBases(theAccess, theFinal, aThisIter.OldShape(),
79                 false, theAdditionalDocument, theResult);
80               aFoundAnyShape = true;
81             }
82           }
83         }
84       }
85     }
86   }
87   if (!aFoundAnyShape && !theAdditionalDocument.IsNull()) { // try to find in additional document
88     static TDF_Label anEmpty;
89     if (TNaming_Tool::HasLabel(theAdditionalDocument, theValue))
90       findBases(theAdditionalDocument, Handle(TNaming_NamedShape)(), theValue,
91         false, anEmpty, theResult);
92   }
93 }
94
95 /// Returns in theResults all shapes with history started in theBase and ended in theFinal
96 static void findFinals(const TDF_Label& anAccess, const TopoDS_Shape& theBase,
97   const TDF_Label& theFinal,
98   const TDF_Label& theAdditionalDoc, TopTools_MapOfShape& thePass, TopTools_MapOfShape& theResults)
99 {
100   if (TNaming_Tool::HasLabel(anAccess, theBase)) {
101     for(TNaming_NewShapeIterator aBaseIter(theBase, anAccess); aBaseIter.More(); aBaseIter.Next())
102     {
103       TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
104       if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
105         if (aBaseIter.NamedShape()->Label().IsEqual(theFinal)) {
106           theResults.Add(aBaseIter.Shape());
107         } else {
108           if (thePass.Add(aBaseIter.Shape()))
109             findFinals(
110               anAccess, aBaseIter.Shape(), theFinal, theAdditionalDoc, thePass, theResults);
111         }
112       }
113     }
114   }
115   if (!theAdditionalDoc.IsNull()) { // search additionally by the additional access label
116     static TDF_Label anEmpty;
117     TopTools_MapOfShape aPass;
118     findFinals(theAdditionalDoc, theBase, theFinal, anEmpty, aPass, theResults);
119   }
120 }
121
122 void Selector_Modify::findModificationResult(TopoDS_ListOfShape& theCommon) {
123   for(TDF_LabelList::Iterator aBase(myBases); aBase.More(); aBase.Next()) {
124     TDF_Label anAdditionalDoc; // this document if base is started in extra document
125     if (aBase.Value().Root() != label().Root()) {
126       anAdditionalDoc = label();
127     }
128     TopTools_MapOfShape aFinals;
129     TopTools_MapOfShape aPass;
130     for(TNaming_Iterator aBaseShape(aBase.Value()); aBaseShape.More(); aBaseShape.Next()) {
131       findFinals(aBase.Value(), aBaseShape.NewShape(), myFinal, anAdditionalDoc, aPass, aFinals);
132     }
133     if (!aFinals.IsEmpty()) {
134       if (theCommon.IsEmpty()) { // just copy all to common
135         for(TopTools_MapOfShape::Iterator aFinal(aFinals); aFinal.More(); aFinal.Next()) {
136           theCommon.Append(aFinal.Key());
137         }
138       } else { // keep only shapes presented in both lists
139         for(TopoDS_ListOfShape::Iterator aCommon(theCommon); aCommon.More(); ) {
140           if (aFinals.Contains(aCommon.Value())) {
141             aCommon.Next();
142           } else { // common is not found, remove it
143             theCommon.Remove(aCommon);
144           }
145         }
146       }
147     }
148   }
149 }
150
151 bool Selector_Modify::select(NCollection_List<Handle(TNaming_NamedShape)>& theModifList,
152   const TopoDS_Shape theContext, const TopoDS_Shape theValue)
153 {
154   if (theModifList.Extent() > 1) { // searching for the best modification result: by context
155     Handle(TNaming_NamedShape) aCandidate;
156     NCollection_List<Handle(TNaming_NamedShape)>::Iterator aModIter(theModifList);
157     for (; !theModifList.IsEmpty() && aModIter.More(); aModIter.Next()) {
158       aCandidate = aModIter.Value();
159       TDF_Label aFatherLab = aCandidate->Label().Father();
160       Handle(TNaming_NamedShape) aFatherNS;
161       if (aFatherLab.FindAttribute(TNaming_NamedShape::GetID(), aFatherNS)) {
162         for (TNaming_Iterator anIter(aFatherNS); anIter.More(); anIter.Next()) {
163           if (theContext.IsSame(anIter.NewShape())) { // found the best modification
164             theModifList.Clear();
165             break;
166           }
167         }
168       }
169     }
170     // take the best candidate, or the last in the iteration
171     theModifList.Clear();
172     theModifList.Append(aCandidate);
173   }
174
175   if (!theModifList.IsEmpty()) {
176     // searching for all the base shapes of this modification
177     findBases(label(), theModifList.First(), theValue, true, baseDocument(), myBases);
178     if (!myBases.IsEmpty()) {
179       myFinal = theModifList.First()->Label();
180       TopoDS_ListOfShape aCommon;
181       findModificationResult(aCommon);
182       // trying to search by neighbors
183       if (aCommon.Extent() > 1) { // more complicated selection
184         if (alwaysGeometricalNaming()) {
185           TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
186           TopoDS_Shape aFirst = aCommonIter.Value();
187           for (aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
188             if (!sameGeometry(aFirst, aCommonIter.Value()))
189               break;
190           }
191           if (!aCommonIter.More()) { // all geometry is same, result is a compound
192             return true;
193           }
194         }
195       } else if (aCommon.Extent() == 1) {
196         return true; // simple modification
197       }
198       if (useNeighbors()) { // optimization: for the current moment only in one case this method is
199                             //  called where this is not needed if neighbors option is disabled
200         // weak naming between the common results
201         Selector_NExplode aNexp(aCommon);
202         myWeakIndex = aNexp.index(theValue);
203       } else
204         myWeakIndex = 0;
205       return myWeakIndex != -1;
206     }
207     // weak naming case
208     TopoDS_ListOfShape aCommon;
209     myFinal = theModifList.First()->Label();
210     Handle(TNaming_NamedShape) aNS;
211     if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
212       for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
213         const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
214         if (!aNewShape.IsNull())
215           aCommon.Append(aNewShape);
216       }
217     }
218     Selector_NExplode aNexp(aCommon);
219     myWeakIndex = aNexp.index(theValue);
220     return myWeakIndex != -1;
221   }
222   return false;
223 }
224
225 void Selector_Modify::store()
226 {
227   storeType(Selector_Algo::SELTYPE_MODIFICATION);
228   storeBaseArray(myBases, myFinal);
229   if (myWeakIndex != -1) {
230     TDataStd_Integer::Set(label(), weakID(), myWeakIndex);
231   }
232 }
233
234 bool Selector_Modify::restore()
235 {
236   if (restoreBaseArray(myBases, myFinal)) {
237     Handle(TDataStd_Integer) aWeakInt;
238     if (label().FindAttribute(weakID(), aWeakInt)) {
239       myWeakIndex = aWeakInt->Get();
240     }
241     return true;
242   }
243   return false;
244 }
245
246 TDF_Label Selector_Modify::restoreByName(std::wstring theName,
247   const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
248 {
249   typedef NCollection_DataMap<TopoDS_Shape, bool, TopTools_ShapeMapHasher> MapOfCompsolids;
250   MapOfCompsolids aWrongSubsCompsolids;
251
252   TDF_Label aContext;
253   for(size_t anEnd, aStart = 0; aStart != std::wstring::npos; aStart = anEnd) {
254     if (aStart != 0)
255       aStart++;
256     anEnd = theName.find(L'&', aStart);
257     std::wstring aSubStr =
258       theName.substr(aStart, anEnd == std::wstring::npos ? anEnd : anEnd - aStart);
259     size_t aFoundOldWeak = aSubStr.find(oldWeakNameID());
260     size_t aFoundNewWeak = aFoundOldWeak != std::wstring::npos ?
261                            aSubStr.find(weakNameID()) :
262                            aFoundOldWeak;
263     if (aFoundOldWeak == 0 || aFoundNewWeak == 0) { // weak name identifier
264       std::wstring aWeakIndex = aSubStr.substr(aFoundOldWeak + oldWeakNameID().size());
265       myWeakIndex = atoi(Locale::Convert::toString(aWeakIndex).c_str());
266       myRecomputeWeakIndex = aFoundOldWeak == 0;
267       continue;
268     }
269     TDF_Label aSubContext, aValue;
270     if (!theNameGenerator->restoreContext(aSubStr, aSubContext, aValue))
271       return TDF_Label(); // can not restore
272     if (aSubContext.IsNull() || (aValue.IsNull() && theShapeType <= TopAbs_SHELL))
273       return TDF_Label(); // can not restore
274     if (myFinal.IsNull()) {
275       myFinal = aValue;
276       aContext = aSubContext;
277     } else {
278       // This could be a solid in a compsolid, which was not modified by the previous operation,
279       // however, the selected subshape is stored on its sub-label by mistake. Thus, wait until
280       // the end of processing to check whether the subshape is found in another solid.
281       TDF_Label aParent = aSubContext.Father().Father();
282       Handle(TNaming_NamedShape) aNS;
283       if (aParent.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
284         TopoDS_Shape aShape = aNS->Get();
285         if (aShape.ShapeType() == TopAbs_COMPSOLID) {
286           if (aWrongSubsCompsolids.IsBound(aShape)) {
287             if (!aValue.IsNull())
288               aWrongSubsCompsolids.Bind(aShape, true);
289           } else
290             aWrongSubsCompsolids.Bind(aShape, !aValue.IsNull());
291         }
292       } else if (aValue.IsNull())
293         return TDF_Label();
294
295       if (!aValue.IsNull())
296         myBases.Append(aValue);
297     }
298   }
299
300   // check all compsolids are processed and names are resolved
301   for (MapOfCompsolids::Iterator anIt(aWrongSubsCompsolids); anIt.More(); anIt.Next())
302     if (!anIt.Value())
303       return TDF_Label();
304
305   return aContext;
306 }
307
308 bool Selector_Modify::solve(const TopoDS_Shape& theContext)
309 {
310   TopoDS_Shape aResult;
311   if (myBases.IsEmpty() && myWeakIndex > 0) { // weak name by the final shapes index
312     TopoDS_ListOfShape aCommon;
313     Handle(TNaming_NamedShape) aNS;
314     if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
315       for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
316         const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
317         if (!aNewShape.IsNull())
318           aCommon.Append(aNewShape);
319       }
320     }
321     Selector_NExplode aNexp(aCommon, myRecomputeWeakIndex);
322     aResult = aNexp.shape(myWeakIndex);
323     myRecomputeWeakIndex = false;
324   } else { // standard case
325     TopoDS_ListOfShape aFinalsCommon; // final shapes presented in all results from bases
326     findModificationResult(aFinalsCommon);
327     if (aFinalsCommon.Extent() == 1) { // result is valid: found only one shape
328       aResult = aFinalsCommon.First();
329       findNewVersion(theContext, aResult);
330     } else if (aFinalsCommon.Extent() > 1 && myWeakIndex > 0) {
331       Selector_NExplode aNExp(aFinalsCommon, myRecomputeWeakIndex);
332       aResult = aNExp.shape(myWeakIndex);
333       myRecomputeWeakIndex = false;
334       findNewVersion(theContext, aResult);
335     } else if (aFinalsCommon.Extent() > 1 && geometricalNaming()) {// if same geometry - compound
336       TopoDS_ListOfShape::Iterator aCommonIter(aFinalsCommon);
337       TopoDS_Shape aFirst = aCommonIter.Value();
338       for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
339         if (!sameGeometry(aFirst, aCommonIter.Value()))
340           break;
341       }
342       if (!aCommonIter.More()) { // all geometry is same, create a result compound
343         TopoDS_Builder aBuilder;
344         TopoDS_Compound aCompound;
345         aBuilder.MakeCompound(aCompound);
346         for(aCommonIter.Initialize(aFinalsCommon); aCommonIter.More(); aCommonIter.Next()) {
347           TopoDS_Shape aSub = aCommonIter.Value();
348           findNewVersion(theContext, aSub);
349           aBuilder.Add(aCompound, aSub);
350         }
351         aResult = aCompound;
352       }
353
354     }
355   }
356
357   if (!aResult.IsNull()) {
358     Selector_Algo::store(aResult);
359     return true;
360   }
361   return false;
362 }
363
364 std::wstring Selector_Modify::name(Selector_NameGenerator* theNameGenerator)
365 {
366   // final&base1&base2 +optionally: [weak_name_1]
367   std::wstring aResult;
368   Handle(TDataStd_Name) aName;
369   if (!myFinal.FindAttribute(TDataStd_Name::GetID(), aName))
370     return L"";
371   aResult += theNameGenerator->contextName(myFinal) + L"/";
372   aResult += Locale::Convert::toWString(aName->Get().ToExtString());
373   for(TDF_LabelList::iterator aBase = myBases.begin(); aBase != myBases.end(); aBase++) {
374     if (!aBase->FindAttribute(TDataStd_Name::GetID(), aName))
375       return L"";
376     aResult += L"&";
377     aResult += theNameGenerator->contextName(*aBase) + L"/";
378     aResult += Locale::Convert::toWString(aName->Get().ToExtString());
379   }
380   if (myWeakIndex != -1) {
381     std::wostringstream aWeakStr;
382     aWeakStr<<L"&"<<weakNameID()<<myWeakIndex;
383     aResult += aWeakStr.str();
384   }
385   return aResult;
386 }