Salome HOME
12f9a0c6c31543656975b3c8863620be7b6d2bff
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_SketchCopy.cpp
1 // Copyright (C) 2020-2023  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 <SketchPlugin_SketchCopy.h>
21 #include <SketchPlugin_Sketch.h>
22 #include <SketchPlugin_SketchEntity.h>
23 #include <SketchPlugin_Tools.h>
24
25 #include <ModelAPI_AttributeRefAttr.h>
26 #include <ModelAPI_AttributeRefAttrList.h>
27 #include <ModelAPI_AttributeReference.h>
28 #include <ModelAPI_AttributeRefList.h>
29 #include <ModelAPI_CompositeFeature.h>
30 #include <ModelAPI_ResultConstruction.h>
31
32 #include <GeomAlgoAPI_Copy.h>
33
34 #include <list>
35 #include <map>
36 #include <string>
37 #include <sstream>
38
39 class MapEntities;
40 static void copyAttribute(AttributePtr theOld, AttributePtr theNew, MapEntities& theMapOldNew);
41
42 /// Internal structure to keep relation between sub-elements of old and new sketch.
43 class MapEntities
44 {
45 public:
46   void bind(FeaturePtr theOld, FeaturePtr theNew)
47   {
48     myObjects[theOld] = theNew;
49     checkPostponed(theOld);
50
51     std::list<ResultPtr> anOldResults = theOld->results();
52     std::list<ResultPtr> aNewResults = theNew->results();
53     for (std::list<ResultPtr>::iterator it1 = anOldResults.begin(), it2 = aNewResults.begin();
54          it1 != anOldResults.end() && it2 != aNewResults.end(); ++it1, ++it2) {
55       myObjects[*it1] = *it2;
56       checkPostponed(*it1);
57     }
58   }
59
60   void postpone(ObjectPtr theOldObject, AttributePtr theOldAttr, AttributePtr theNewAttr)
61   {
62     if (theOldObject)
63       myPostponed[theOldObject][theOldAttr] = theNewAttr;
64   }
65
66   ObjectPtr find(ObjectPtr theOld)
67   {
68     auto aFound = myObjects.find(theOld);
69     return aFound == myObjects.end() ? ObjectPtr() : aFound->second;
70   }
71
72   AttributePtr find(AttributePtr theOld)
73   {
74     FeaturePtr anOldOwner = ModelAPI_Feature::feature(theOld->owner());
75     FeaturePtr aNewOwner = ModelAPI_Feature::feature(find(anOldOwner));
76     return aNewOwner ? aNewOwner->attribute(theOld->id()) : AttributePtr();
77   }
78
79 protected:
80   void checkPostponed(ObjectPtr theOld)
81   {
82     auto aFound = myPostponed.find(theOld);
83     if (aFound == myPostponed.end())
84       return;
85     for (auto it = aFound->second.begin(); it != aFound->second.end(); ++it)
86       copyAttribute(it->first, it->second, *this);
87     myPostponed.erase(aFound);
88   }
89
90 private:
91   std::map<ObjectPtr, ObjectPtr> myObjects;
92   std::map<ObjectPtr, std::map<AttributePtr, AttributePtr> > myPostponed;
93 };
94
95
96 SketchPlugin_SketchCopy::SketchPlugin_SketchCopy() : ModelAPI_Feature()
97 {}
98
99 void SketchPlugin_SketchCopy::initAttributes()
100 {
101   data()->addAttribute(BASE_ID(), ModelAPI_AttributeReference::typeId());
102 }
103
104 static void copyRefAttr(AttributeRefAttrPtr theOld,
105                         AttributeRefAttrPtr theNew,
106                         MapEntities& theMapOldNew)
107 {
108   if (theOld->isObject()) {
109     ObjectPtr aNew = theMapOldNew.find(theOld->object());
110     if (aNew)
111       theNew->setObject(aNew);
112     else
113       theMapOldNew.postpone(theOld->object(), theOld, theNew);
114   }
115   else {
116     AttributePtr aNewAttr = theMapOldNew.find(theOld->attr());
117     if (aNewAttr)
118       theNew->setAttr(aNewAttr);
119     else
120       theMapOldNew.postpone(theOld->attr()->owner(), theOld, theNew);
121   }
122 }
123
124 static void copyRefAttrList(AttributeRefAttrListPtr theOld,
125                             AttributeRefAttrListPtr theNew,
126                             MapEntities& theMapOldNew)
127 {
128   // copy only if all referred objects/attribute are already transfered
129   std::list<std::pair<ObjectPtr, AttributePtr> > aRefs = theOld->list();
130   for (std::list<std::pair<ObjectPtr, AttributePtr> >::iterator anIt = aRefs.begin();
131        anIt != aRefs.end(); ++anIt) {
132     ObjectPtr aNewObj = anIt->first ? theMapOldNew.find(anIt->first) : ObjectPtr();
133     AttributePtr aNewAttr = anIt->second ? theMapOldNew.find(anIt->second) : AttributePtr();
134     if (aNewObj || aNewAttr)
135       *anIt = std::pair<ObjectPtr, AttributePtr>(aNewObj, aNewAttr);
136     else {
137       if (anIt->first)
138         theMapOldNew.postpone(anIt->first, theOld, theNew);
139       else
140         theMapOldNew.postpone(anIt->second->owner(), theOld, theNew);
141       return;
142     }
143   }
144   // update the RefAttrList
145   theNew->clear();
146   for (std::list<std::pair<ObjectPtr, AttributePtr> >::iterator anIt = aRefs.begin();
147        anIt != aRefs.end(); ++anIt) {
148     if (anIt->first)
149       theNew->append(anIt->first);
150     else
151       theNew->append(anIt->second);
152   }
153 }
154
155 static void copyReference(AttributeReferencePtr theOld,
156                           AttributeReferencePtr theNew,
157                           MapEntities& theMapOldNew)
158 {
159   ObjectPtr aNew = theMapOldNew.find(theOld->value());
160   if (aNew)
161     theNew->setValue(aNew);
162   else
163     theMapOldNew.postpone(theOld->value(), theOld, theNew);
164 }
165
166 static void copyRefList(AttributeRefListPtr theOld,
167                         AttributeRefListPtr theNew,
168                         MapEntities& theMapOldNew)
169 {
170   // copy only if all referred objects are already transfered
171   std::list<ObjectPtr> aRefs = theOld->list();
172   for (std::list<ObjectPtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
173     ObjectPtr aNew = theMapOldNew.find(*anIt);
174     if (aNew)
175       *anIt = aNew;
176     else {
177       theMapOldNew.postpone(*anIt, theOld, theNew);
178       return;
179     }
180   }
181   // update theRefList
182   theNew->clear();
183   for (std::list<ObjectPtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt)
184     theNew->append(*anIt);
185 }
186
187 static void copySelection(AttributeSelectionPtr theOld,
188                           AttributeSelectionPtr theNew)
189 {
190   theNew->selectValue(theOld);
191 }
192
193 void copyAttribute(AttributePtr theOld, AttributePtr theNew, MapEntities& theMapOldNew)
194 {
195   if (theNew->attributeType() == ModelAPI_AttributeRefAttr::typeId()) {
196     copyRefAttr(std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theOld),
197                 std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theNew),
198                 theMapOldNew);
199   }
200   else if (theNew->attributeType() == ModelAPI_AttributeReference::typeId()) {
201     copyReference(std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theOld),
202                   std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theNew),
203                   theMapOldNew);
204   }
205   else if (theNew->attributeType() == ModelAPI_AttributeRefAttrList::typeId()) {
206     copyRefAttrList(std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theOld),
207                     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theNew),
208                     theMapOldNew);
209   }
210   else if (theNew->attributeType() == ModelAPI_AttributeRefList::typeId()) {
211     copyRefList(std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theOld),
212                 std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theNew),
213                 theMapOldNew);
214   }
215   else if (theNew->attributeType() == ModelAPI_AttributeSelection::typeId()) {
216     copySelection(std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theOld),
217                   std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theNew));
218   }
219 }
220
221 static void renameByParent(FeaturePtr theOld, FeaturePtr theNew)
222 {
223   AttributeReferencePtr anOldParentRef = theOld->reference(SketchPlugin_SketchEntity::PARENT_ID());
224   if (!anOldParentRef || !anOldParentRef->isInitialized())
225     return;
226
227   AttributeReferencePtr aNewParentRef = theNew->reference(SketchPlugin_SketchEntity::PARENT_ID());
228
229   std::wstring anOldName = anOldParentRef->value()->data()->name();
230   std::wstring aNewName = aNewParentRef->value()->data()->name();
231
232   // substitute name of old parent by the new one
233   theNew->data()->setName(theOld->name());
234   SketchPlugin_Tools::replaceInName(theNew, anOldName, aNewName);
235
236   const std::list<ResultPtr>& anOldResults = theOld->results();
237   const std::list<ResultPtr>& aNewResults = theNew->results();
238   for (std::list<ResultPtr>::const_iterator it0 = anOldResults.begin(), it1 = aNewResults.begin();
239        it0 != anOldResults.end() && it1 != aNewResults.end(); ++it0, ++it1) {
240     (*it1)->data()->setName((*it0)->data()->name());
241     SketchPlugin_Tools::replaceInName(*it1, anOldName, aNewName);
242   }
243 }
244
245 static void copyFeature(const FeaturePtr theFeature,
246                         std::shared_ptr<SketchPlugin_Sketch> theSketch,
247                         MapEntities& theMapOldNew)
248 {
249   if (!theFeature)
250     return;
251
252   // copy feature and its results
253   FeaturePtr aNewFeature = theSketch->addFeature(theFeature->getKind());
254   theFeature->data()->copyTo(aNewFeature->data());
255   int aResultIndex = 0;
256   for (std::list<ResultPtr>::const_iterator aRIt = theFeature->results().begin();
257        aRIt != theFeature->results().end(); ++aRIt) {
258     ResultConstructionPtr aResult =
259         theSketch->document()->createConstruction(aNewFeature->data(), aResultIndex);
260
261     GeomAlgoAPI_Copy aCopyAlgo((*aRIt)->shape());
262
263     aResult->setShape(aCopyAlgo.shape());
264     aResult->setIsInHistory((*aRIt)->isInHistory());
265     aNewFeature->setResult(aResult, aResultIndex++);
266   }
267   theMapOldNew.bind(theFeature, aNewFeature);
268
269   // update referred features and attributes
270   bool aWasBlocked = aNewFeature->data()->blockSendAttributeUpdated(true, false);
271   std::list<AttributePtr> anAttrs =
272       aNewFeature->data()->attributes(std::string());
273   for (std::list<AttributePtr>::iterator anIt = anAttrs.begin(); anIt != anAttrs.end(); ++anIt) {
274     AttributePtr anOldAttr = theFeature->attribute((*anIt)->id());
275     copyAttribute(anOldAttr, *anIt, theMapOldNew);
276   }
277   aNewFeature->data()->blockSendAttributeUpdated(aWasBlocked, false);
278
279   // rename feature according to parent
280   renameByParent(theFeature, aNewFeature);
281 }
282
283 static int index(const std::wstring& theName, const std::wstring& thePrefix)
284 {
285   int anIndex = -1;
286   if (theName.find(thePrefix) == 0) {
287     anIndex = 0;
288     if (theName[thePrefix.size()] == '_') {
289       std::wstring anIndexStr = theName.substr(thePrefix.size() + 1);
290       anIndex = std::stoi(anIndexStr);
291     }
292   }
293   return anIndex;
294 }
295
296
297 void SketchPlugin_SketchCopy::execute()
298 {
299   FeaturePtr aBaseSketchFeature = ModelAPI_Feature::feature(reference(BASE_ID())->value());
300   CompositeFeaturePtr aBaseSketch =
301       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aBaseSketchFeature);
302   if (!aBaseSketch) {
303     setError("Error: a base feature is not a sketch.");
304     return; // invalid case
305   }
306
307   FeaturePtr aNewSketchFeature = document()->addFeature(aBaseSketch->getKind());
308   std::shared_ptr<SketchPlugin_Sketch> aNewSketch =
309       std::dynamic_pointer_cast<SketchPlugin_Sketch>(aNewSketchFeature);
310   // copy all attributes of sketch, then clear sub-features to be able to copy then one by one
311   aBaseSketch->data()->copyTo(aNewSketch->data());
312   aNewSketch->reflist(SketchPlugin_Sketch::FEATURES_ID())->clear();
313
314   MapEntities aMapOldNew;
315
316   std::list<ObjectPtr> aBaseFeatures =
317       aBaseSketch->reflist(SketchPlugin_Sketch::FEATURES_ID())->list();
318   for (std::list<ObjectPtr>::const_iterator aFIt = aBaseFeatures.begin();
319        aFIt != aBaseFeatures.end(); ++aFIt) {
320     FeaturePtr aCurFeature = ModelAPI_Feature::feature(*aFIt);
321     copyFeature(aCurFeature, aNewSketch, aMapOldNew);
322   }
323
324   aNewSketch->execute();
325
326   // check number of copies of the selected sketch before name the new sketch
327   static const std::wstring SKETCH_NAME_SUFFIX(L"_Copy");
328   std::wstring aSketchName = aBaseSketch->name() + SKETCH_NAME_SUFFIX;
329   int aNewSketchIndex = 0;
330   std::list<FeaturePtr> aFeatures = document()->allFeatures();
331   for (std::list<FeaturePtr>::iterator aFIt = aFeatures.begin(); aFIt != aFeatures.end(); ++aFIt) {
332     if ((*aFIt)->getKind() != SketchPlugin_Sketch::ID())
333       continue;
334     int anIndex = index((*aFIt)->name(), aSketchName);
335     if (anIndex >= aNewSketchIndex)
336       aNewSketchIndex = anIndex + 1;
337     anIndex = index((*aFIt)->lastResult()->data()->name(), aSketchName);
338     if (anIndex >= aNewSketchIndex)
339       aNewSketchIndex = anIndex + 1;
340   }
341   std::wostringstream aNameStream;
342   aNameStream << aSketchName;
343   if (aNewSketchIndex > 0)
344     aNameStream << '_' << aNewSketchIndex;
345   aNewSketch->data()->setName(aNameStream.str());
346   aNewSketch->lastResult()->data()->setName(aNameStream.str());
347 }