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