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