Salome HOME
Fix for the issue #2723 : result is shown in viewer after remove feature with parameter
[modules/shaper.git] / src / Model / Model_ResultConstruction.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include <Model_ResultConstruction.h>
22
23 #include <Model_Data.h>
24 #include <ModelAPI_CompositeFeature.h>
25 #include <GeomAlgoAPI_SketchBuilder.h>
26 #include <ModelAPI_Events.h>
27 #include <Model_Document.h>
28 #include <GeomAPI_PlanarEdges.h>
29 #include <GeomAPI_Shape.h>
30 #include <Events_Loop.h>
31 #include <GeomDataAPI_Point.h>
32 #include <GeomDataAPI_Dir.h>
33
34 #include <TDF_ChildIDIterator.hxx>
35 #include <TNaming_NamedShape.hxx>
36 #include <TNaming_Builder.hxx>
37 #include <TDataStd_IntPackedMap.hxx>
38 #include <TDataStd_Name.hxx>
39 #include <TDataStd_UAttribute.hxx>
40 #include <BRep_Builder.hxx>
41 #include <TopoDS.hxx>
42 #include <TopoDS_Edge.hxx>
43 #include <TopoDS_Vertex.hxx>
44 #include <TopoDS_ListOfShape.hxx>
45 #include <TopExp_Explorer.hxx>
46 #include <TopTools_MapOfShape.hxx>
47 #include <NCollection_IndexedDataMap.hxx>
48
49 #include <algorithm>
50
51
52 // identifier of the infinite result
53 Standard_GUID kIS_INFINITE("dea8cc5a-53f2-49c1-94e8-a947bed20a9f");
54 // identifier of the result not in history
55 Standard_GUID kIS_IN_HISTORY("a9aec01c-805e-44d1-b5d2-a63f06522f8a");
56
57
58 void Model_ResultConstruction::colorConfigInfo(std::string& theSection, std::string& theName,
59                                        std::string& theDefault)
60 {
61   theSection = "Visualization";
62   theName = "result_construction_color";
63   theDefault = DEFAULT_COLOR();
64 }
65
66 void Model_ResultConstruction::setShape(std::shared_ptr<GeomAPI_Shape> theShape)
67 {
68   if (myShape != theShape) {
69     if (!isInfinite())
70       storeShape(theShape);
71     if (!theShape.get() || !theShape->isEqual(myShape)) {
72       static const Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
73       ModelAPI_EventCreator::get()->sendUpdated(data()->owner(), anEvent);
74     }
75     myShape = theShape;
76   }
77 }
78
79 std::shared_ptr<GeomAPI_Shape> Model_ResultConstruction::shape()
80 {
81   return myShape;
82 }
83
84 static std::string shortName(
85   std::shared_ptr<ModelAPI_ResultConstruction>& theConstr)
86 {
87   std::string aName = theConstr->data()->name();
88   // remove "-", "/" and "&" command-symbols
89   aName.erase(std::remove(aName.begin(), aName.end(), '-'), aName.end());
90   aName.erase(std::remove(aName.begin(), aName.end(), '/'), aName.end());
91   aName.erase(std::remove(aName.begin(), aName.end(), '&'), aName.end());
92   // remove the last 's', 'e', 'f' and 'r' symbols:
93   // they are used as markers of start/end/forward/reversed indicators
94   static const std::string aSyms("sefr");
95   std::string::iterator aSuffix = aName.end() - 1;
96   while(aSyms.find(*aSuffix) != std::string::npos) {
97     --aSuffix;
98   }
99   aName.erase(aSuffix + 1, aName.end());
100   return aName;
101 }
102
103 bool Model_ResultConstruction::updateShape()
104 {
105   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
106   if (aData && aData->isValid()) {
107     TDF_Label aShapeLab = aData->shapeLab();
108     Handle(TNaming_NamedShape) aNS;
109     if (aShapeLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
110       TopoDS_Shape aShape = aNS->Get();
111       if (!aShape.IsNull()) {
112         if (aShape.ShapeType() == TopAbs_COMPOUND) {
113           // restore the sketch planar edges object
114           std::shared_ptr<GeomAPI_PlanarEdges> aBigWire(new GeomAPI_PlanarEdges);
115           aBigWire->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
116           FeaturePtr aSketch =
117             document()->feature(std::dynamic_pointer_cast<ModelAPI_Result>(data()->owner()));
118           std::shared_ptr<GeomDataAPI_Point> anOrigin =
119             std::dynamic_pointer_cast<GeomDataAPI_Point>(aSketch->data()->attribute("Origin"));
120           std::shared_ptr<GeomDataAPI_Dir> aDirX =
121             std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute("DirX"));
122           std::shared_ptr<GeomDataAPI_Dir> aNorm =
123             std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute("Norm"));
124           if (anOrigin.get() && aDirX.get() && aNorm.get()) {
125             aBigWire->setPlane(anOrigin->pnt(), aDirX->dir(), aNorm->dir());
126             myShape = aBigWire;
127             return true;
128           }
129         }
130         // just restore shape
131         GeomShapePtr aGShape(new GeomAPI_Shape);
132         aGShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
133         myShape = aGShape; // restore the sketch sub-components
134         return true;
135       }
136     }
137   }
138   return false;
139 }
140
141 Model_ResultConstruction::Model_ResultConstruction()
142 {
143 }
144
145 bool Model_ResultConstruction::isInHistory()
146 {
147   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
148   if (aData.get() && aData->isValid()) {
149     return !aData->label().IsAttribute(kIS_IN_HISTORY); // by default no attribute, but in history
150   }
151   return true;  // unknown case
152 }
153
154 void Model_ResultConstruction::setIsInHistory(const bool isInHistory)
155 {
156   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
157   if (aData.get() && aData->isValid()) {
158     if (!isInHistory)
159       TDataStd_UAttribute::Set(aData->label(), kIS_IN_HISTORY);
160     else
161       aData->label().ForgetAttribute(kIS_IN_HISTORY);
162   }
163 }
164
165 bool Model_ResultConstruction::isInfinite()
166 {
167   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
168   if (aData.get() && aData->isValid()) {
169     return aData->label().IsAttribute(kIS_INFINITE);
170   }
171   return false;  // unknown case
172 }
173
174 void Model_ResultConstruction::setInfinite(const bool theInfinite)
175 {
176   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
177   if (aData.get() && aData->isValid()) {
178     if (theInfinite)
179       TDataStd_UAttribute::Set(aData->label(), kIS_INFINITE);
180     else
181       aData->label().ForgetAttribute(kIS_INFINITE);
182   }
183 }
184
185 int Model_ResultConstruction::facesNum(const bool theUpdateNaming)
186 {
187   int aResult = 0;
188   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
189   if (aData.get() && aData->isValid()) {
190     TDF_Label aShapeLab = aData->shapeLab();
191     TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
192     for (; anOldIter.More(); anOldIter.Next()) {
193       aResult++;
194     }
195   }
196   return aResult;
197 }
198
199 std::shared_ptr<GeomAPI_Face> Model_ResultConstruction::face(const int theIndex)
200 {
201   std::shared_ptr<GeomAPI_Face> aResult;
202   int anIndex = 0;
203   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
204   if (aData.get() && aData->isValid()) {
205     TDF_Label aShapeLab = aData->shapeLab();
206     TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
207     for (; anOldIter.More(); anOldIter.Next()) {
208       if (anIndex == theIndex) {
209         Handle(TNaming_NamedShape) aNS;
210         anOldIter.Value()->Label().FindAttribute(TNaming_NamedShape::GetID(), aNS);
211         aResult.reset(new GeomAPI_Face);
212         aResult->setImpl(new TopoDS_Shape(aNS->Get()));
213         break;
214       }
215       anIndex++;
216     }
217   }
218   return aResult;
219 }
220
221 void Model_ResultConstruction::setIsConcealed(const bool theValue)
222 {
223   // do nothing: the construction element is never concealed
224 }
225
226 void Model_ResultConstruction::storeShape(std::shared_ptr<GeomAPI_Shape> theShape)
227 {
228   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
229   if (aData && aData->isValid()) {
230     std::string aMyName = data()->name();
231     TDF_Label aShapeLab = aData->shapeLab();
232     if (!theShape.get() || theShape->isNull()) {
233       aShapeLab.ForgetAllAttributes();
234       TDataStd_Name::Set(aShapeLab, aMyName.c_str()); // restore name forgotten
235       return;
236     }
237     std::shared_ptr<Model_Document> aMyDoc =
238       std::dynamic_pointer_cast<Model_Document>(document());
239     const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
240     if (aShape.ShapeType() == TopAbs_VERTEX) {
241       aShapeLab.ForgetAllAttributes(); // clear all previously stored
242       TNaming_Builder aBuilder(aShapeLab);
243       aBuilder.Generated(aShape);
244       TDataStd_Name::Set(aShapeLab, aMyName.c_str());
245       aMyDoc->addNamingName(aShapeLab, aMyName);
246     } else if (aShape.ShapeType() == TopAbs_EDGE) { // store sub-vertices on sub-labels
247       aShapeLab.ForgetAllAttributes(); // clear all previously stored
248       TNaming_Builder aBuilder(aShapeLab);
249       aBuilder.Generated(aShape);
250
251       TopExp_Explorer anExp(aShape, TopAbs_VERTEX);
252       for(int anIndex = 1; anExp.More(); anExp.Next(), anIndex++) {
253         TDF_Label aSubLab = aShapeLab.FindChild(anIndex);
254         TNaming_Builder aBuilder(aSubLab);
255         aBuilder.Generated(anExp.Current());
256         std::string aVertexName = aMyName + "_" + (anIndex == 1 ? "StartVertex" : "EndVertex");
257         TDataStd_Name::Set(aSubLab, aVertexName.c_str());
258         aMyDoc->addNamingName(aSubLab, aVertexName);
259       }
260       TDataStd_Name::Set(aShapeLab, aMyName.c_str());
261       aMyDoc->addNamingName(aShapeLab, aMyName);
262     } else { // this is probably sketch, so, work with it as with composite
263       std::shared_ptr<GeomAPI_PlanarEdges> aWirePtr =
264         std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theShape);
265       if (!aWirePtr.get())
266         return; // unknown case
267       ResultPtr aThisPtr = std::dynamic_pointer_cast<ModelAPI_Result>(data()->owner());
268       FeaturePtr aThisFeature = aMyDoc->feature(aThisPtr);
269       CompositeFeaturePtr aComposite =
270         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aThisFeature);
271       if (!aComposite || aComposite->numberOfSubs() == 0)
272         return; // unknown case
273       // collect indices of curves of current composite
274       NCollection_DataMap<Handle(Geom_Curve), int> aCurvesIndices;
275       NCollection_DataMap<int, TopoDS_Edge> anEdgeIndices;
276       std::map<int, std::string> aComponentsNames; // names of components that lay on index
277       const int aSubNum = aComposite->numberOfSubs();
278       for (int a = 0; a < aSubNum; a++) {
279         FeaturePtr aSub = aComposite->subFeature(a);
280         const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
281         std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResults.cbegin();
282         for (; aRes != aResults.cend(); aRes++) {
283           ResultConstructionPtr aConstr =
284             std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
285           if (aConstr->shape() && aConstr->shape()->isEdge()) {
286             TopoDS_Edge anEdge = TopoDS::Edge(aConstr->shape()->impl<TopoDS_Shape>());
287             Standard_Real aFirst, aLast;
288             Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
289             aCurvesIndices.Bind(aCurve, a);
290             anEdgeIndices.Bind(a, anEdge);
291             aComponentsNames[a] = shortName(aConstr);
292           }
293         }
294       }
295
296       std::list<std::shared_ptr<GeomAPI_Shape> > aFaces;
297       GeomAlgoAPI_SketchBuilder::createFaces(aWirePtr->origin(), aWirePtr->dirX(),
298         aWirePtr->norm(), aWirePtr, aFaces);
299       // order is important to store faces in the same order if sketch is created from scratch
300       NCollection_IndexedDataMap<TopoDS_Face, TColStd_ListOfInteger> aNewIndices; // edges indices
301       std::list<std::shared_ptr<GeomAPI_Shape> >::iterator aFIter = aFaces.begin();
302       for (; aFIter != aFaces.end(); aFIter++) {
303         std::shared_ptr<GeomAPI_Face> aFace(new GeomAPI_Face(*aFIter));
304         // put them to a label, trying to keep the same faces on the same labels
305         if (aFace.get() && !aFace->isNull()) {
306           TopoDS_Face aTopoFace = TopoDS::Face(aFace->impl<TopoDS_Shape>());
307           aNewIndices.Add(aTopoFace, TColStd_ListOfInteger());
308           // keep new indices of sub-elements used in this face
309           for (TopExp_Explorer anEdges(aTopoFace, TopAbs_EDGE); anEdges.More(); anEdges.Next()) {
310             TopoDS_Edge anEdge = TopoDS::Edge(anEdges.Current());
311             Standard_Real aFirst, aLast;
312             Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
313             if (aCurvesIndices.IsBound(aCurve)) {
314               int anIndex = aCurvesIndices.Find(aCurve);
315               if ((aFirst > aLast) != (anEdge.Orientation() == TopAbs_REVERSED))
316                 anIndex = -anIndex;
317               aNewIndices.ChangeFromKey(aTopoFace).Append(anIndex);
318             }
319           }
320         }
321       }
322       NCollection_DataMap<int, TopoDS_Face> aFacesOrder; // faces -> tag where they must be set
323       NCollection_List<TopoDS_Face> anUnorderedFaces; // faces that may be located at any index
324       // searching for the best new candidate to old location
325       NCollection_IndexedDataMap<TopoDS_Face, TColStd_ListOfInteger>::Iterator
326         aNewIter(aNewIndices);
327       for (; aNewIter.More(); aNewIter.Next()) {
328         double aBestFound = 0, aBestNotFound = 1.e+100;
329         int aBestTag = 0;
330         const TColStd_ListOfInteger& aNewInd = aNewIter.Value();
331         // old faces indices where they where located
332         TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
333         for (; anOldIter.More(); anOldIter.Next()) {
334           int aTag = anOldIter.Value()->Label().Tag();
335           if (aFacesOrder.IsBound(aTag))
336             continue; // already found a best candidate
337           Handle(TDataStd_IntPackedMap) anOldIndices =
338             Handle(TDataStd_IntPackedMap)::DownCast(anOldIter.Value());
339           double aFound = 0, aNotFound = 0;
340           TColStd_ListOfInteger::Iterator aNewIndIter(aNewInd);
341           for (; aNewIndIter.More(); aNewIndIter.Next()) {
342             if (anOldIndices->Contains(aNewIndIter.Value())) {
343               aFound += 1.;
344             }
345             else if (anOldIndices->Contains(-aNewIndIter.Value())) { // different orientation
346               aFound += 0.001;
347             }
348             else {
349               aNotFound += 1.;
350             }
351           }
352           if (aNotFound < aBestNotFound) {
353             if (aFound > aBestFound) {
354               aBestNotFound = aNotFound;
355               aBestFound = aFound;
356               aBestTag = aTag;
357             }
358           }
359         }
360         if (aBestTag != 0) { // found an appropriate face
361           aFacesOrder.Bind(aBestTag, aNewIter.Key());
362         } else {
363           anUnorderedFaces.Append(aNewIter.Key());
364         }
365       }
366       aShapeLab.ForgetAllAttributes(); // clear all previously stored
367       TDataStd_Name::Set(aShapeLab, aMyName.c_str()); // restore name forgotten
368       TNaming_Builder aBuilder(aShapeLab); // store the compound to get it ready on open of document
369       aBuilder.Generated(aShape);
370       aMyDoc->addNamingName(aShapeLab, aMyName);
371       // set new faces to the labels
372       int aCurrentTag = 1;
373       NCollection_List<TopoDS_Face>::Iterator anUnordered(anUnorderedFaces);
374       for(int aCurrentTag = 1; !aFacesOrder.IsEmpty() || anUnordered.More(); aCurrentTag++) {
375         TopoDS_Face aFaceToPut;
376         if (aFacesOrder.IsBound(aCurrentTag)) {
377           aFaceToPut = aFacesOrder.Find(aCurrentTag);
378           aFacesOrder.UnBind(aCurrentTag);
379         } else if (anUnordered.More()){
380           aFaceToPut = anUnordered.Value();
381           anUnordered.Next();
382         }
383
384         if (!aFaceToPut.IsNull()) {
385           TopTools_MapOfShape aFaceEdges;
386           for(TopExp_Explorer anEdges(aFaceToPut, TopAbs_EDGE); anEdges.More(); anEdges.Next()) {
387             aFaceEdges.Add(anEdges.Current());
388           }
389
390           TDF_Label aLab = aShapeLab.FindChild(aCurrentTag);
391           TNaming_Builder aFaceBuilder(aLab);
392           aFaceBuilder.Generated(aFaceToPut);
393           // store also indices of the new face edges
394           Handle(TDataStd_IntPackedMap) aNewMap = TDataStd_IntPackedMap::Set(aLab);
395           const TColStd_ListOfInteger& aNewInd = aNewIndices.FindFromKey(aFaceToPut);
396           std::stringstream aName;
397           aName<<"Face";
398           TopExp_Explorer aPutEdges(aFaceToPut, TopAbs_EDGE);
399           TNaming_Builder* anEdgesBuilder = 0;
400           for(TColStd_ListOfInteger::Iterator anIter(aNewInd); anIter.More(); anIter.Next()) {
401             int anIndex = anIter.Value();
402             int aModIndex = anIndex > 0 ? anIndex : -anIndex;
403             aNewMap->Add(anIndex);
404             aName<<"-"<<aComponentsNames[aModIndex];
405             if (anIter.Value() > 0)
406               aName<<"f";
407             else
408               aName<<"r";
409             // collect all edges of the face which are modified in sub-label of the face
410             if (anEdgeIndices.IsBound(aModIndex) &&
411                 !aFaceEdges.Contains(anEdgeIndices.Find(aModIndex))) {
412               if (!anEdgesBuilder) {
413                 TDF_Label anEdgesLabel = aLab.FindChild(1);
414                 anEdgesBuilder = new TNaming_Builder(anEdgesLabel);
415                 std::ostringstream aSubName;
416                 // tag is needed for Test1922 to distinguish sub-edges of different faces
417                 aSubName<<"SubEdge_"<<aCurrentTag;
418                 TDataStd_Name::Set(anEdgesLabel, aSubName.str().c_str());
419               }
420               anEdgesBuilder->Modify(anEdgeIndices.Find(aModIndex), aPutEdges.Current());
421             }
422             aPutEdges.Next();
423           }
424           TDataStd_Name::Set(aLab, TCollection_ExtendedString(aName.str().c_str()));
425           aMyDoc->addNamingName(aLab, aName.str());
426           // put also wires to sub-labels to correctly select them instead of collection by edges
427           int aWireTag = 2; // first tag is for SubEdge-s
428           for(TopExp_Explorer aWires(aFaceToPut, TopAbs_WIRE); aWires.More(); aWires.Next()) {
429             TDF_Label aWireLab = aLab.FindChild(aWireTag);
430             TNaming_Builder aWireBuilder(aWireLab);
431             aWireBuilder.Generated(aWires.Current());
432             std::ostringstream aWireName;
433             aWireName<<aName.str()<<"_wire";
434             if (aWireTag > 2)
435               aWireName<<"_"<<aWireTag - 1;
436             TDataStd_Name::Set(aWireLab, aWireName.str().c_str());
437             aMyDoc->addNamingName(aWireLab, aWireName.str());
438             aWireTag++;
439           }
440         }
441       }
442     }
443   }
444 }