Salome HOME
Implementation of SketchCopy feature
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_SketchCopy.cpp
1 // Copyright (C) 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 <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                           MapEntities& theMapOldNew)
190 {
191   theNew->selectValue(theOld);
192 }
193
194 void copyAttribute(AttributePtr theOld, AttributePtr theNew, MapEntities& theMapOldNew)
195 {
196   if (theNew->attributeType() == ModelAPI_AttributeRefAttr::typeId()) {
197     copyRefAttr(std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theOld),
198                 std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theNew),
199                 theMapOldNew);
200   }
201   else if (theNew->attributeType() == ModelAPI_AttributeReference::typeId()) {
202     copyReference(std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theOld),
203                   std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theNew),
204                   theMapOldNew);
205   }
206   else if (theNew->attributeType() == ModelAPI_AttributeRefAttrList::typeId()) {
207     copyRefAttrList(std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theOld),
208                     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theNew),
209                     theMapOldNew);
210   }
211   else if (theNew->attributeType() == ModelAPI_AttributeRefList::typeId()) {
212     copyRefList(std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theOld),
213                 std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theNew),
214                 theMapOldNew);
215   }
216   else if (theNew->attributeType() == ModelAPI_AttributeSelection::typeId()) {
217     copySelection(std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theOld),
218                   std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theNew),
219                   theMapOldNew);
220   }
221 }
222
223 static void renameByParent(FeaturePtr theOld, FeaturePtr theNew)
224 {
225   AttributeReferencePtr anOldParentRef = theOld->reference(SketchPlugin_SketchEntity::PARENT_ID());
226   if (!anOldParentRef || !anOldParentRef->isInitialized())
227     return;
228
229   AttributeReferencePtr aNewParentRef = theNew->reference(SketchPlugin_SketchEntity::PARENT_ID());
230
231   std::string anOldName = anOldParentRef->value()->data()->name();
232   std::string aNewName = aNewParentRef->value()->data()->name();
233
234   // substitute name of old parent by the new one
235   theNew->data()->setName(theOld->name());
236   SketchPlugin_Tools::replaceInName(theNew, anOldName, aNewName);
237
238   const std::list<ResultPtr>& anOldResults = theOld->results();
239   const std::list<ResultPtr>& aNewResults = theNew->results();
240   for (std::list<ResultPtr>::const_iterator it0 = anOldResults.begin(), it1 = aNewResults.begin();
241        it0 != anOldResults.end() && it1 != aNewResults.end(); ++it0, ++it1) {
242     (*it1)->data()->setName((*it0)->data()->name());
243     SketchPlugin_Tools::replaceInName(*it1, anOldName, aNewName);
244   }
245 }
246
247 static void copyFeature(const FeaturePtr theFeature,
248                         std::shared_ptr<SketchPlugin_Sketch> theSketch,
249                         MapEntities& theMapOldNew)
250 {
251   if (!theFeature)
252     return;
253
254   // copy feature and its results
255   FeaturePtr aNewFeature = theSketch->addFeature(theFeature->getKind());
256   theFeature->data()->copyTo(aNewFeature->data());
257   int aResultIndex = 0;
258   for (std::list<ResultPtr>::const_iterator aRIt = theFeature->results().begin();
259        aRIt != theFeature->results().end(); ++aRIt) {
260     ResultConstructionPtr aResult =
261         theSketch->document()->createConstruction(aNewFeature->data(), aResultIndex);
262
263     GeomAlgoAPI_Copy aCopyAlgo((*aRIt)->shape());
264
265     aResult->setShape(aCopyAlgo.shape());
266     aResult->setIsInHistory((*aRIt)->isInHistory());
267     aNewFeature->setResult(aResult, aResultIndex++);
268   }
269   theMapOldNew.bind(theFeature, aNewFeature);
270
271   // update referred features and attributes
272   bool aWasBlocked = aNewFeature->data()->blockSendAttributeUpdated(true, false);
273   std::list<AttributePtr> anAttrs =
274       aNewFeature->data()->attributes(std::string());
275   for (std::list<AttributePtr>::iterator anIt = anAttrs.begin(); anIt != anAttrs.end(); ++anIt) {
276     AttributePtr anOldAttr = theFeature->attribute((*anIt)->id());
277     copyAttribute(anOldAttr, *anIt, theMapOldNew);
278   }
279   aNewFeature->data()->blockSendAttributeUpdated(aWasBlocked, false);
280
281   // rename feature according to parent
282   renameByParent(theFeature, aNewFeature);
283 }
284
285 static int index(const std::string& theName, const std::string& thePrefix)
286 {
287   int anIndex = -1;
288   if (theName.find(thePrefix) == 0) {
289     anIndex = 0;
290     if (theName[thePrefix.size()] == '_') {
291       std::string anIndexStr = theName.substr(thePrefix.size() + 1);
292       anIndex = std::atoi(anIndexStr.c_str());
293     }
294   }
295   return anIndex;
296 }
297
298
299 void SketchPlugin_SketchCopy::execute()
300 {
301   FeaturePtr aBaseSketchFeature = ModelAPI_Feature::feature(reference(BASE_ID())->value());
302   CompositeFeaturePtr aBaseSketch =
303       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aBaseSketchFeature);
304   if (!aBaseSketch) {
305     setError("Error: a base feature is not a sketch.");
306     return; // invalid case
307   }
308
309   FeaturePtr aNewSketchFeature = document()->addFeature(aBaseSketch->getKind());
310   std::shared_ptr<SketchPlugin_Sketch> aNewSketch =
311       std::dynamic_pointer_cast<SketchPlugin_Sketch>(aNewSketchFeature);
312   // copy all attributes of sketch, then clear sub-features to be able to copy then one by one
313   aBaseSketch->data()->copyTo(aNewSketch->data());
314   aNewSketch->reflist(SketchPlugin_Sketch::FEATURES_ID())->clear();
315
316   MapEntities aMapOldNew;
317
318   std::list<ObjectPtr> aBaseFeatures =
319       aBaseSketch->reflist(SketchPlugin_Sketch::FEATURES_ID())->list();
320   for (std::list<ObjectPtr>::const_iterator aFIt = aBaseFeatures.begin();
321        aFIt != aBaseFeatures.end(); ++aFIt) {
322     FeaturePtr aCurFeature = ModelAPI_Feature::feature(*aFIt);
323     copyFeature(aCurFeature, aNewSketch, aMapOldNew);
324   }
325
326   aNewSketch->execute();
327
328   // check number of copies of the selected sketch before name the new sketch
329   static const std::string SKETCH_NAME_SUFFIX("_Copy");
330   std::string aSketchName = aBaseSketch->name() + SKETCH_NAME_SUFFIX;
331   int aNewSketchIndex = 0;
332   std::list<FeaturePtr> aFeatures = document()->allFeatures();
333   for (std::list<FeaturePtr>::iterator aFIt = aFeatures.begin(); aFIt != aFeatures.end(); ++aFIt) {
334     if ((*aFIt)->getKind() != SketchPlugin_Sketch::ID())
335       continue;
336     int anIndex = index((*aFIt)->name(), aSketchName);
337     if (anIndex >= aNewSketchIndex)
338       aNewSketchIndex = anIndex + 1;
339     anIndex = index((*aFIt)->lastResult()->data()->name(), aSketchName);
340     if (anIndex >= aNewSketchIndex)
341       aNewSketchIndex = anIndex + 1;
342   }
343   std::ostringstream aNameStream;
344   aNameStream << aSketchName;
345   if (aNewSketchIndex > 0)
346     aNameStream << '_' << aNewSketchIndex;
347   aNewSketch->data()->setName(aNameStream.str());
348   aNewSketch->lastResult()->data()->setName(aNameStream.str());
349 }