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