Salome HOME
aa3d7c9760acdd0b0c11ff4f2790944655ef0656
[modules/shaper.git] / src / Model / Model_AttributeSelection.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_AttributeSelection.h
4 // Created:     2 Oct 2014
5 // Author:      Mikhail PONIKAROV
6
7 #include "Model_AttributeSelection.h"
8 #include "Model_Application.h"
9 #include "Model_Events.h"
10 #include "Model_Data.h"
11 #include <ModelAPI_Feature.h>
12 #include <ModelAPI_ResultBody.h>
13 #include <ModelAPI_ResultConstruction.h>
14 #include <ModelAPI_CompositeFeature.h>
15 #include <GeomAPI_Shape.h>
16 #include <GeomAPI_PlanarEdges.h>
17 #include <GeomAlgoAPI_SketchBuilder.h>
18 #include <Events_Error.h>
19
20 #include <TNaming_Selector.hxx>
21 #include <TNaming_NamedShape.hxx>
22 #include <TNaming_Tool.hxx>
23 #include <TNaming_Builder.hxx>
24 #include <TopoDS_Shape.hxx>
25 #include <TDataStd_IntPackedMap.hxx>
26 #include <TDataStd_Integer.hxx>
27 #include <TopTools_MapOfShape.hxx>
28 #include <TopExp_Explorer.hxx>
29 #include <TDF_LabelMap.hxx>
30 #include <BRep_Tool.hxx>
31 #include <TopoDS_Edge.hxx>
32 #include <TopoDS.hxx>
33 #include <TColStd_MapOfTransient.hxx>
34 #include <gp_Pnt.hxx>
35 #include <Precision.hxx>
36 #include <TDF_ChildIterator.hxx>
37 #include <TDF_ChildIDIterator.hxx>
38 #include <TDataStd_Name.hxx>
39 #include <TopAbs_ShapeEnum.hxx>
40 #include <TopoDS_Iterator.hxx>
41 using namespace std;
42 /// adeed to the index in the packed map to signalize that the vertex of edge is seleted
43 /// (multiplied by the index of the edge)
44 static const int kSTART_VERTEX_DELTA = 1000000;
45
46 // on this label is stored:
47 // TNaming_NamedShape - selected shape
48 // TNaming_Naming - topological selection information (for the body)
49 // TDataStd_IntPackedMap - indexes of edges in composite element (for construction)
50 // TDataStd_Integer - type of the selected shape (for construction)
51 // TDF_Reference - from ReferenceAttribute, the context
52
53 void Model_AttributeSelection::setValue(const ResultPtr& theContext,
54   const std::shared_ptr<GeomAPI_Shape>& theSubShape)
55 {
56   const std::shared_ptr<GeomAPI_Shape>& anOldShape = value();
57   bool isOldShape = 
58     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
59   if (isOldShape) return; // shape is the same, so context is also unchanged
60   // update the referenced object if needed
61   bool isOldContext = theContext == myRef.value();
62   if (!isOldContext)
63     myRef.setValue(theContext);
64
65   if (theContext->groupName() == ModelAPI_ResultBody::group())
66     selectBody(theContext, theSubShape);
67   else if (theContext->groupName() == ModelAPI_ResultConstruction::group())
68     selectConstruction(theContext, theSubShape);
69
70   std::string aSelName = buildSubShapeName(theSubShape, theContext);
71   if(!aSelName.empty())
72           TDataStd_Name::Set(selectionLabel(), aSelName.c_str()); //set name
73
74   myIsInitialized = true;
75   owner()->data()->sendAttributeUpdated(this);
76 }
77
78 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
79 {
80   std::shared_ptr<GeomAPI_Shape> aResult;
81   if (myIsInitialized) {
82     Handle(TNaming_NamedShape) aSelection;
83     if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
84       TopoDS_Shape aSelShape = aSelection->Get();
85       aResult = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
86       aResult->setImpl(new TopoDS_Shape(aSelShape));
87     } else { // for simple construction element: just shape of this construction element
88       ResultConstructionPtr aConstr = 
89         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(context());
90       if (aConstr) {
91         return aConstr->shape();
92       }
93     }
94   }
95   return aResult;
96 }
97
98 Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel)
99   : myRef(theLabel)
100 {
101   myIsInitialized = myRef.isInitialized();
102 }
103
104 ResultPtr Model_AttributeSelection::context() {
105   return std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
106 }
107
108
109 void Model_AttributeSelection::setObject(const std::shared_ptr<ModelAPI_Object>& theObject)
110 {
111   ModelAPI_AttributeSelection::setObject(theObject);
112   myRef.setObject(theObject);
113 }
114
115 TDF_LabelMap& Model_AttributeSelection::scope()
116 {
117   if (myScope.IsEmpty()) { // create a new scope if not yet done
118     // gets all labels with named shapes that are bofore this feature label (before in history)
119     TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
120       owner()->data())->label().Father();
121     int aFeatureID = aFeatureLab.Tag();
122     TDF_ChildIterator aFeaturesIter(aFeatureLab.Father());
123     for(; aFeaturesIter.More(); aFeaturesIter.Next()) {
124       if (aFeaturesIter.Value().Tag() >= aFeatureID) // the left labels are created later
125         break;
126       TDF_ChildIDIterator aNSIter(aFeaturesIter.Value(), TNaming_NamedShape::GetID(), 1);
127       for(; aNSIter.More(); aNSIter.Next()) {
128         Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
129         if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
130           myScope.Add(aNS->Label());
131         }
132       }
133     }
134   }
135   return myScope;
136 }
137
138 bool Model_AttributeSelection::update()
139 {
140   ResultPtr aContext = context();
141   if (!aContext) return false;
142   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
143     // body: just a named shape, use selection mechanism from OCCT
144     TNaming_Selector aSelector(selectionLabel());
145     bool aResult = aSelector.Solve(scope()) == Standard_True;
146     owner()->data()->sendAttributeUpdated(this);
147     return aResult;
148   } else if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
149     // construction: identification by the results indexes, recompute faces and
150     // take the face that more close by the indexes
151     std::shared_ptr<GeomAPI_PlanarEdges> aWirePtr = 
152       std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(
153       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext)->shape());
154     if (aWirePtr && aWirePtr->hasPlane()) { // sketch sub-element
155       TDF_Label aLab = myRef.myRef->Label();
156       // getting a type of selected shape
157       Handle(TDataStd_Integer) aTypeAttr;
158       if (!aLab.FindAttribute(TDataStd_Integer::GetID(), aTypeAttr)) {
159         return false;
160       }
161       TopAbs_ShapeEnum aShapeType = (TopAbs_ShapeEnum)(aTypeAttr->Get());
162       // selected indexes will be needed in each "if"
163       Handle(TDataStd_IntPackedMap) aSubIds;
164       std::shared_ptr<GeomAPI_Shape> aNewSelected;
165       bool aNoIndexes = 
166         !aLab.FindAttribute(TDataStd_IntPackedMap::GetID(), aSubIds) || aSubIds->Extent() == 0;
167       // for now working only with composite features
168       FeaturePtr aContextFeature = aContext->document()->feature(aContext);
169       CompositeFeaturePtr aComposite = 
170         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature);
171       if (!aComposite || aComposite->numberOfSubs() == 0) {
172         return false;
173       }
174
175       if (aShapeType == TopAbs_FACE) {
176         // If this is a wire with plane defined thin it is a sketch-like object
177         std::list<std::shared_ptr<GeomAPI_Shape> > aFaces;
178         GeomAlgoAPI_SketchBuilder::createFaces(aWirePtr->origin(), aWirePtr->dirX(),
179           aWirePtr->dirY(), aWirePtr->norm(), aWirePtr, aFaces);
180         if (aFaces.empty()) // no faces, update can not work correctly
181           return false;
182         // if there is no edges indexes, any face can be used: take the first
183         std::shared_ptr<GeomAPI_Shape> aNewSelected;
184         if (aNoIndexes) {
185           aNewSelected = *(aFaces.begin());
186         } else { // searching for most looks-like initial face by the indexes
187           // prepare edges of the current resut for the fast searching
188           TColStd_MapOfTransient allCurves;
189           const int aSubNum = aComposite->numberOfSubs();
190           for(int a = 0; a < aSubNum; a++) {
191             if (aSubIds->Contains(aComposite->subFeatureId(a))) {
192               FeaturePtr aSub = aComposite->subFeature(a);
193               const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
194               std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes;
195               for(aRes = aResults.cbegin(); aRes != aResults.cend(); aRes++) {
196                 ResultConstructionPtr aConstr = 
197                   std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
198                 if (aConstr->shape() && aConstr->shape()->isEdge()) {
199                   const TopoDS_Shape& aResShape = aConstr->shape()->impl<TopoDS_Shape>();
200                   TopoDS_Edge anEdge = TopoDS::Edge(aResShape);
201                   if (!anEdge.IsNull()) {
202                     Standard_Real aFirst, aLast;
203                     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
204                     allCurves.Add(aCurve);
205                   }
206                 }
207               }
208             }
209           }
210           // iterate new result faces and searching for these edges
211           std::list<std::shared_ptr<GeomAPI_Shape> >::iterator aFacesIter = aFaces.begin();
212           double aBestFound = 0; // best percentage of found edges
213           for(; aFacesIter != aFaces.end(); aFacesIter++) {
214             int aFound = 0, aNotFound = 0;
215             TopExp_Explorer anEdgesExp((*aFacesIter)->impl<TopoDS_Shape>(), TopAbs_EDGE);
216             for(; anEdgesExp.More(); anEdgesExp.Next()) {
217               TopoDS_Edge anEdge = TopoDS::Edge(anEdgesExp.Current());
218               if (!anEdge.IsNull()) {
219                 Standard_Real aFirst, aLast;
220                 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
221                 if (allCurves.Contains(aCurve)) {
222                   aFound++;
223                 } else {
224                   aNotFound++;
225                 }
226               }
227             }
228             if (aFound + aNotFound != 0) {
229               double aPercentage = double(aFound) / double(aFound + aNotFound);
230               if (aPercentage > aBestFound) {
231                 aBestFound = aPercentage;
232                 aNewSelected = *aFacesIter;
233               }
234             }
235           }
236         }
237         if (aNewSelected) { // store this new selection
238           selectConstruction(aContext, aNewSelected);
239           owner()->data()->sendAttributeUpdated(this);
240           return true;
241         }
242       } else if (aShapeType == TopAbs_EDGE) {
243         // just reselect the edge by the id
244         const int aSubNum = aComposite->numberOfSubs();
245         for(int a = 0; a < aSubNum; a++) {
246           // if aSubIds take any, the first appropriate
247           if (aSubIds->IsEmpty() || aSubIds->Contains(aComposite->subFeatureId(a))) {
248             // found the appropriate feature
249             FeaturePtr aFeature = aComposite->subFeature(a);
250             std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aResIter =
251               aFeature->results().cbegin();
252             for(;aResIter != aFeature->results().cend(); aResIter++) {
253               ResultConstructionPtr aRes = 
254                 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIter);
255               if (aRes && aRes->shape() && aRes->shape()->isEdge()) { // found!
256                 selectConstruction(aContext, aRes->shape());
257                 owner()->data()->sendAttributeUpdated(this);
258                 return true;
259               }
260             }
261           }
262         }
263       } else if (aShapeType == TopAbs_VERTEX) {
264         // just reselect the vertex by the id of edge
265         const int aSubNum = aComposite->numberOfSubs();
266         for(int a = 0; a < aSubNum; a++) {
267           // if aSubIds take any, the first appropriate
268           int aFeatureID = aComposite->subFeatureId(a);
269           if (aSubIds->IsEmpty() || aSubIds->Contains(aFeatureID)) {
270             // searching for deltas
271             int aVertexNum = 0;
272             if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA)) aVertexNum = 1;
273             else if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA * 2)) aVertexNum = 2;
274             // found the feature with appropriate edge
275             FeaturePtr aFeature = aComposite->subFeature(a);
276             std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aResIter =
277               aFeature->results().cbegin();
278             for(;aResIter != aFeature->results().cend(); aResIter++) {
279               ResultConstructionPtr aRes = 
280                 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIter);
281               if (aRes && aRes->shape()) {
282                 if (aRes->shape()->isVertex() && aVertexNum == 0) { // found!
283                   selectConstruction(aContext, aRes->shape());
284                   owner()->data()->sendAttributeUpdated(this);
285                   return true;
286                 } else if (aRes->shape()->isEdge() && aVertexNum > 0) {
287                   const TopoDS_Shape& anEdge = aRes->shape()->impl<TopoDS_Shape>();
288                   int aVIndex = 1;
289                   for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) {
290                     if (aVIndex == aVertexNum) { // found!
291                       std::shared_ptr<GeomAPI_Shape> aVertex(new GeomAPI_Shape);
292                       aVertex->setImpl(new TopoDS_Shape(aVExp.Current()));
293                       selectConstruction(aContext, aVertex);
294                       owner()->data()->sendAttributeUpdated(this);
295                       return true;
296                     }
297                     aVIndex++;
298                   }
299                 }
300               }
301             }
302           }
303         }
304       }
305     } else { // simple construction element: the selected is that needed
306       owner()->data()->sendAttributeUpdated(this);
307       return true;
308     }
309   }
310   return false; // unknown case
311 }
312
313
314 void Model_AttributeSelection::selectBody(
315     const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
316 {
317   // perform the selection
318   TNaming_Selector aSel(selectionLabel());
319   TopoDS_Shape aNewShape = theSubShape ? theSubShape->impl<TopoDS_Shape>() : TopoDS_Shape();
320   TopoDS_Shape aContext;
321
322   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(myRef.value());
323   if (aBody)
324     aContext = aBody->shape()->impl<TopoDS_Shape>();
325   else {
326     ResultConstructionPtr aConstr = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(myRef.value());
327     if (aConstr) {
328       aContext = aConstr->shape()->impl<TopoDS_Shape>();
329     } else {
330       Events_Error::send("A result with shape is expected");
331       return;
332     }
333   }
334   aSel.Select(aNewShape, aContext);
335 }
336
337 void Model_AttributeSelection::selectConstruction(
338     const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
339 {
340   FeaturePtr aContextFeature = theContext->document()->feature(theContext);
341   CompositeFeaturePtr aComposite = 
342     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature);
343   if (!aComposite || aComposite->numberOfSubs() == 0) {
344     return; // saving of context is enough: result construction contains exactly the needed shape
345   }
346   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(owner()->data());
347   TDF_Label aLab = myRef.myRef->Label();
348   // identify the reuslts of sub-object of the composite by edges
349   const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
350   // save type of the selected shape in integer attribute
351   TopAbs_ShapeEnum aShapeType = aSubShape.ShapeType();
352   TDataStd_Integer::Set(aLab, (int)aShapeType);
353   gp_Pnt aVertexPos;
354   TColStd_MapOfTransient allCurves;
355   if (aShapeType == TopAbs_VERTEX) { // compare positions
356     aVertexPos = BRep_Tool::Pnt(TopoDS::Vertex(aSubShape));
357   } else { 
358     for(TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) {
359       TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current());
360       Standard_Real aFirst, aLast;
361       Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
362       allCurves.Add(aCurve);
363     }
364   }
365   // iterate and store the result ids of sub-elements
366   Handle(TDataStd_IntPackedMap) aRefs = TDataStd_IntPackedMap::Set(aLab);
367   aRefs->Clear();
368   const int aSubNum = aComposite->numberOfSubs();
369   for(int a = 0; a < aSubNum; a++) {
370     FeaturePtr aSub = aComposite->subFeature(a);
371     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
372     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResults.cbegin();
373     // there may be many shapes (circle and center): register if at least one is in selection
374     for(; aRes != aResults.cend(); aRes++) {
375       ResultConstructionPtr aConstr = 
376         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
377       if (!aConstr->shape()) {
378         continue;
379       }
380       if (aShapeType == TopAbs_VERTEX) {
381         if (aConstr->shape()->isVertex()) { // compare vertices positions
382           const TopoDS_Shape& aVertex = aConstr->shape()->impl<TopoDS_Shape>();
383           gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVertex));
384           if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) {
385             aRefs->Add(aComposite->subFeatureId(a));
386           }
387         } else { // get first or last vertex of the edge: last is stored with negative sign
388           const TopoDS_Shape& anEdge = aConstr->shape()->impl<TopoDS_Shape>();
389           int aDelta = kSTART_VERTEX_DELTA;
390           for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) {
391             gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVExp.Current()));
392             if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) {
393               aRefs->Add(aComposite->subFeatureId(a));
394               aRefs->Add(aDelta + aComposite->subFeatureId(a));
395               break;
396             }
397             aDelta += kSTART_VERTEX_DELTA;
398           }
399         }
400       } else {
401         if (aConstr->shape()->isEdge()) {
402           const TopoDS_Shape& aResShape = aConstr->shape()->impl<TopoDS_Shape>();
403           TopoDS_Edge anEdge = TopoDS::Edge(aResShape);
404           if (!anEdge.IsNull()) {
405             Standard_Real aFirst, aLast;
406             Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
407             if (allCurves.Contains(aCurve)) {
408               int anID = aComposite->subFeatureId(a);
409               aRefs->Add(anID);
410               // add edges to sub-label to support naming for edges selection
411               for(TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) {
412                 TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current());
413                 Standard_Real aFirst, aLast;
414                 Handle(Geom_Curve) aFaceCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
415                 if (aFaceCurve == aCurve) {
416                   TNaming_Builder anEdgeBuilder(selectionLabel().FindChild(anID));
417                   anEdgeBuilder.Generated(anEdge);
418                 }
419               }
420             }
421           }
422         }
423       }
424     }
425   }
426   // store the selected as primitive
427   TNaming_Builder aBuilder(selectionLabel());
428   aBuilder.Generated(aSubShape);
429 }
430
431 TDF_Label Model_AttributeSelection::selectionLabel()
432 {
433   return myRef.myRef->Label().FindChild(1);
434 }
435
436 std::string Model_AttributeSelection::buildSubShapeName(std::shared_ptr<GeomAPI_Shape> theSubShape, 
437                                                             const ResultPtr& theContext)
438 {
439   std::string aName;
440   if(theSubShape->isNull() || theContext->shape()->isNull()) return aName;  
441   TopoDS_Shape aSubShape = theSubShape->impl<TopoDS_Shape>();
442   TopoDS_Shape aContext  = theContext->shape()->impl<TopoDS_Shape>();
443
444   // check if the subShape is already in DF
445   Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aSubShape, selectionLabel());
446   Handle(TDataStd_Name) anAttr;
447   if(!aNS.IsNull()) { // in the document    
448         if(aNS->Label().FindAttribute(TDataStd_Name::GetID(), anAttr)) {
449           aName = TCollection_AsciiString(anAttr->Get()).ToCString();
450           if(!aName.empty()) {
451             std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(theContext->document());
452             const TDF_Label& aLabel = aDoc->findNamingName(aName);
453                 if(!aLabel.IsEqual(aNS->Label())) {
454                         aName.erase(); //something is wrong, to be checked!
455                 }
456                 const TopoDS_Shape& aShape = aNS->Get();
457                 if(aShape.ShapeType() == TopAbs_COMPOUND) {
458                   std::string aFullName = aName + "_";
459                   TopoDS_Iterator it(aShape);
460                         int i(1);
461                         for (;it.More();it.Next(), i++) {
462                           if(it.Value() != aSubShape) continue;
463                           else {
464                                 aName = aFullName + TCollection_AsciiString (i).ToCString();
465                                 break;
466                           }
467                         }
468                 }
469           }     
470         }
471   }
472   if(aName.empty() ) { // not in the document!
473     TopAbs_ShapeEnum aType = aSubShape.ShapeType();
474         switch (aType) {
475           case TopAbs_FACE:
476       // the Face should be in DF. If it is not a case it is an error ==> to be dbugged
477                 
478                 break;
479           case TopAbs_EDGE:
480                   break;
481           case TopAbs_VERTEX:
482                   break;
483
484         }
485   }
486   return aName;
487 }