1 // Copyright (C) 2014-2023 CEA, EDF
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_Modify.h>
22 #include <Selector_NameGenerator.h>
23 #include <Selector_NExplode.h>
25 #include <Locale_Convert.h>
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>
39 #include <TopoDS_Builder.hxx>
40 #include <TopoDS_Compound.hxx>
42 Selector_Modify::Selector_Modify() : Selector_Algo()
44 myWeakIndex = -1; // no index by default
45 myRecomputeWeakIndex = false;
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)
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))
70 if (!aResIter.More()) // not found, so add this new
71 theResult.Append(aResult);
72 aFoundAnyShape = true;
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;
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);
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)
100 if (TNaming_Tool::HasLabel(anAccess, theBase)) {
101 for(TNaming_NewShapeIterator aBaseIter(theBase, anAccess); aBaseIter.More(); aBaseIter.Next())
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());
108 if (thePass.Add(aBaseIter.Shape()))
110 anAccess, aBaseIter.Shape(), theFinal, theAdditionalDoc, thePass, theResults);
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);
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();
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);
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());
138 } else { // keep only shapes presented in both lists
139 for(TopoDS_ListOfShape::Iterator aCommon(theCommon); aCommon.More(); ) {
140 if (aFinals.Contains(aCommon.Value())) {
142 } else { // common is not found, remove it
143 theCommon.Remove(aCommon);
151 bool Selector_Modify::select(NCollection_List<Handle(TNaming_NamedShape)>& theModifList,
152 const TopoDS_Shape theContext, const TopoDS_Shape theValue)
154 if (theModifList.Extent() > 1) { // searching for the best modification result: by context
155 bool isFound = false;
156 Handle(TNaming_NamedShape) aCandidate;
157 NCollection_List<Handle(TNaming_NamedShape)>::Iterator aModIter(theModifList);
158 for (; aModIter.More() && !isFound; aModIter.Next()) {
159 aCandidate = aModIter.Value();
160 TDF_Label aFatherLab = aCandidate->Label().Father();
161 Handle(TNaming_NamedShape) aFatherNS;
162 if (aFatherLab.FindAttribute(TNaming_NamedShape::GetID(), aFatherNS)) {
163 for (TNaming_Iterator anIter(aFatherNS);
164 anIter.More() && !isFound; anIter.Next()) {
165 if (theContext.IsSame(anIter.NewShape())) { // found the best modification
171 // take the best candidate, or the last in the iteration
172 theModifList.Clear();
173 theModifList.Append(aCandidate);
176 if (!theModifList.IsEmpty()) {
177 // searching for all the base shapes of this modification
178 findBases(label(), theModifList.First(), theValue, true, baseDocument(), myBases);
179 if (!myBases.IsEmpty()) {
180 myFinal = theModifList.First()->Label();
181 TopoDS_ListOfShape aCommon;
182 findModificationResult(aCommon);
183 // trying to search by neighbors
184 if (aCommon.Extent() > 1) { // more complicated selection
185 if (alwaysGeometricalNaming()) {
186 TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
187 TopoDS_Shape aFirst = aCommonIter.Value();
188 for (aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
189 if (!sameGeometry(aFirst, aCommonIter.Value()))
192 if (!aCommonIter.More()) { // all geometry is same, result is a compound
196 } else if (aCommon.Extent() == 1) {
197 return true; // simple modification
199 if (useNeighbors()) { // optimization: for the current moment only in one case this method is
200 // called where this is not needed if neighbors option is disabled
201 // weak naming between the common results
202 Selector_NExplode aNexp(aCommon);
203 myWeakIndex = aNexp.index(theValue);
206 return myWeakIndex != -1;
209 TopoDS_ListOfShape aCommon;
210 myFinal = theModifList.First()->Label();
211 Handle(TNaming_NamedShape) aNS;
212 if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
213 for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
214 const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
215 if (!aNewShape.IsNull())
216 aCommon.Append(aNewShape);
219 Selector_NExplode aNexp(aCommon);
220 myWeakIndex = aNexp.index(theValue);
221 return myWeakIndex != -1;
226 void Selector_Modify::store()
228 storeType(Selector_Algo::SELTYPE_MODIFICATION);
229 storeBaseArray(myBases, myFinal);
230 if (myWeakIndex != -1) {
231 TDataStd_Integer::Set(label(), weakID(), myWeakIndex);
235 bool Selector_Modify::restore()
237 if (restoreBaseArray(myBases, myFinal)) {
238 Handle(TDataStd_Integer) aWeakInt;
239 if (label().FindAttribute(weakID(), aWeakInt)) {
240 myWeakIndex = aWeakInt->Get();
247 TDF_Label Selector_Modify::restoreByName(std::wstring theName,
248 const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
250 typedef NCollection_DataMap<TopoDS_Shape, bool, TopTools_ShapeMapHasher> MapOfCompsolids;
251 MapOfCompsolids aWrongSubsCompsolids;
254 for(size_t anEnd, aStart = 0; aStart != std::wstring::npos; aStart = anEnd) {
257 anEnd = theName.find(L'&', aStart);
258 std::wstring aSubStr =
259 theName.substr(aStart, anEnd == std::wstring::npos ? anEnd : anEnd - aStart);
260 size_t aFoundOldWeak = aSubStr.find(oldWeakNameID());
261 size_t aFoundNewWeak = aFoundOldWeak != std::wstring::npos ?
262 aSubStr.find(weakNameID()) :
264 if (aFoundOldWeak == 0 || aFoundNewWeak == 0) { // weak name identifier
265 std::wstring aWeakIndex = aSubStr.substr(aFoundOldWeak + oldWeakNameID().size());
266 myWeakIndex = atoi(Locale::Convert::toString(aWeakIndex).c_str());
267 myRecomputeWeakIndex = aFoundOldWeak == 0;
270 TDF_Label aSubContext, aValue;
271 if (!theNameGenerator->restoreContext(aSubStr, aSubContext, aValue))
272 return TDF_Label(); // can not restore
273 if (aSubContext.IsNull() || (aValue.IsNull() && theShapeType <= TopAbs_SHELL))
274 return TDF_Label(); // can not restore
275 if (myFinal.IsNull()) {
277 aContext = aSubContext;
279 // This could be a solid in a compsolid, which was not modified by the previous operation,
280 // however, the selected subshape is stored on its sub-label by mistake. Thus, wait until
281 // the end of processing to check whether the subshape is found in another solid.
282 TDF_Label aParent = aSubContext.Father().Father();
283 Handle(TNaming_NamedShape) aNS;
284 if (aParent.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
285 TopoDS_Shape aShape = aNS->Get();
286 if (aShape.ShapeType() == TopAbs_COMPSOLID) {
287 if (aWrongSubsCompsolids.IsBound(aShape)) {
288 if (!aValue.IsNull())
289 aWrongSubsCompsolids.Bind(aShape, true);
291 aWrongSubsCompsolids.Bind(aShape, !aValue.IsNull());
293 } else if (aValue.IsNull())
296 if (!aValue.IsNull())
297 myBases.Append(aValue);
301 // check all compsolids are processed and names are resolved
302 for (MapOfCompsolids::Iterator anIt(aWrongSubsCompsolids); anIt.More(); anIt.Next())
309 bool Selector_Modify::solve(const TopoDS_Shape& theContext)
311 TopoDS_Shape aResult;
312 if (myBases.IsEmpty() && myWeakIndex > 0) { // weak name by the final shapes index
313 TopoDS_ListOfShape aCommon;
314 Handle(TNaming_NamedShape) aNS;
315 if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
316 for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
317 const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
318 if (!aNewShape.IsNull())
319 aCommon.Append(aNewShape);
322 Selector_NExplode aNexp(aCommon, myRecomputeWeakIndex);
323 aResult = aNexp.shape(myWeakIndex);
324 myRecomputeWeakIndex = false;
325 } else { // standard case
326 TopoDS_ListOfShape aFinalsCommon; // final shapes presented in all results from bases
327 findModificationResult(aFinalsCommon);
328 if (aFinalsCommon.Extent() == 1) { // result is valid: found only one shape
329 aResult = aFinalsCommon.First();
330 findNewVersion(theContext, aResult);
331 } else if (aFinalsCommon.Extent() > 1 && myWeakIndex > 0) {
332 Selector_NExplode aNExp(aFinalsCommon, myRecomputeWeakIndex);
333 aResult = aNExp.shape(myWeakIndex);
334 myRecomputeWeakIndex = false;
335 findNewVersion(theContext, aResult);
336 } else if (aFinalsCommon.Extent() > 1 && geometricalNaming()) {// if same geometry - compound
337 TopoDS_ListOfShape::Iterator aCommonIter(aFinalsCommon);
338 TopoDS_Shape aFirst = aCommonIter.Value();
339 for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
340 if (!sameGeometry(aFirst, aCommonIter.Value()))
343 if (!aCommonIter.More()) { // all geometry is same, create a result compound
344 TopoDS_Builder aBuilder;
345 TopoDS_Compound aCompound;
346 aBuilder.MakeCompound(aCompound);
347 for(aCommonIter.Initialize(aFinalsCommon); aCommonIter.More(); aCommonIter.Next()) {
348 TopoDS_Shape aSub = aCommonIter.Value();
349 findNewVersion(theContext, aSub);
350 aBuilder.Add(aCompound, aSub);
358 if (!aResult.IsNull()) {
359 Selector_Algo::store(aResult);
365 std::wstring Selector_Modify::name(Selector_NameGenerator* theNameGenerator)
367 // final&base1&base2 +optionally: [weak_name_1]
368 std::wstring aResult;
369 Handle(TDataStd_Name) aName;
370 if (!myFinal.FindAttribute(TDataStd_Name::GetID(), aName))
372 aResult += theNameGenerator->contextName(myFinal) + L"/";
373 aResult += Locale::Convert::toWString(aName->Get().ToExtString());
374 for(TDF_LabelList::iterator aBase = myBases.begin(); aBase != myBases.end(); aBase++) {
375 if (!aBase->FindAttribute(TDataStd_Name::GetID(), aName))
378 aResult += theNameGenerator->contextName(*aBase) + L"/";
379 aResult += Locale::Convert::toWString(aName->Get().ToExtString());
381 if (myWeakIndex != -1) {
382 std::wostringstream aWeakStr;
383 aWeakStr<<L"&"<<weakNameID()<<myWeakIndex;
384 aResult += aWeakStr.str();