Salome HOME
Useful methods for sketch operations managements
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintManager.cpp
1 // File:    SketchSolver_ConstraintManager.cpp
2 // Created: 08 May 2014
3 // Author:  Artem ZHIDKOV
4
5 #include "SketchSolver_ConstraintManager.h"
6
7 #include <Events_Loop.h>
8 #include <GeomDataAPI_Dir.h>
9 #include <GeomDataAPI_Point.h>
10 #include <GeomDataAPI_Point2D.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <Model_Events.h>
15 #include <SketchPlugin_Constraint.h>
16 #include <SketchPlugin_ConstraintCoincidence.h>
17 #include <SketchPlugin_Line.h>
18 #include <SketchPlugin_Sketch.h>
19
20 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
21
22 /// Global constaint manager object
23 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
24
25 /// This value is used to give unique index to the groups
26 static Slvs_hGroup myGroupIndexer = 0;
27
28 /** \brief Makes transformation from ModelAPI_Attribute to the list of parameters' values
29  *  \remark Convertion of normal in 3D needs two attributes (coordinate axis of transversal plane)
30  *  \param[in,out] theParams        list of converted values which appended to incoming list
31  *  \param[in]     theAttr          attribute to be converted
32  *  \param[in]     theNormExtraAttr additional attribute for conversion of a normal
33  */
34 static void ConvertAttributeToParamList(
35         std::vector<double>&                  theParams, 
36         boost::shared_ptr<ModelAPI_Attribute> theAttr, 
37         boost::shared_ptr<ModelAPI_Attribute> theNormExtraAttr = boost::shared_ptr<ModelAPI_Attribute>());
38
39 /** \brief Search the entity/parameter with specified ID in the list of elements
40  *  \param[in] theEntityID unique ID of the element
41  *  \param[in] theEntities list of elements
42  *  \return position of the found element or -1 if the element is not found
43  */
44 template <typename T>
45 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
46
47
48 // ========================================================
49 // ========= SketchSolver_ConstraintManager ===============
50 // ========================================================
51 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
52 {
53   if (!_self)
54     _self = new SketchSolver_ConstraintManager();
55   return _self;
56 }
57
58 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
59 {
60   myGroups.clear();
61
62   // Register in event loop
63   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
64   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
65   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
66 }
67
68 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
69 {
70   myGroups.clear();
71 }
72
73 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
74 {
75   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED))
76   {
77     const Model_FeatureUpdatedMessage* aCreateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
78
79     // Only sketches and constraints can be added by Create event
80     boost::shared_ptr<SketchPlugin_Sketch> aSketch = 
81       boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aCreateMsg->feature());
82     if (aSketch)
83     {
84       addWorkplane(aSketch);
85       return ;
86     }
87     boost::shared_ptr<SketchPlugin_Constraint> aConstraint = 
88       boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aCreateMsg->feature());
89     if (aConstraint)
90     {
91       addConstraint(aConstraint);
92       return ;
93     }
94   }
95   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
96   {
97     const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
98     /// \todo Implement deleting objects on event
99   }
100   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED))
101   {
102     const Model_FeatureUpdatedMessage* aUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
103
104     boost::shared_ptr<SketchPlugin_Sketch> aSketch = 
105       boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aUpdateMsg->feature());
106     if (aSketch)
107     {
108       updateWorkplane(aSketch);
109       return ;
110     }
111
112     boost::shared_ptr<SketchPlugin_Constraint> aConstraint = 
113       boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aUpdateMsg->feature());
114     if (aConstraint)
115     {
116 //      updateConstraint(aConstraint);
117       return ;
118     }
119
120     boost::shared_ptr<SketchPlugin_Feature> aFeature = 
121       boost::dynamic_pointer_cast<SketchPlugin_Feature>(aUpdateMsg->feature());
122 //    if (aFeature)
123 //      updateEntity(aFeature);
124   }
125 }
126
127
128 bool SketchSolver_ConstraintManager::addWorkplane(boost::shared_ptr<SketchPlugin_Sketch> theSketch)
129 {
130   // Check the specified workplane is already used
131   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
132   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
133     if (aGroupIter->isBaseWorkplane(theSketch))
134       return true;
135   // Create new group for workplane
136   SketchSolver_ConstraintGroup aNewGroup(theSketch);
137   // Verify that the group is created successfully
138   if (!aNewGroup.isBaseWorkplane(theSketch))
139     return false;
140   myGroups.push_back(aNewGroup);
141   return true;
142 }
143
144 bool SketchSolver_ConstraintManager::updateWorkplane(boost::shared_ptr<SketchPlugin_Sketch> theSketch)
145 {
146   bool aResult = true; // changed when a workplane wrongly updated
147   bool isUpdated = false;
148   // Try to update specified workplane in all groups
149   std::vector<SketchSolver_ConstraintGroup>::iterator aGroupIter;
150   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
151     if (aGroupIter->isBaseWorkplane(theSketch))
152     {
153       isUpdated = true;
154       if (!aGroupIter->updateWorkplane(theSketch))
155         aResult = false;
156     }
157   // If the workplane is not updates, so this is a new workplane
158   if (!isUpdated)
159     return addWorkplane(theSketch);
160   return aResult;
161 }
162
163 bool SketchSolver_ConstraintManager::addConstraint(
164               boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
165 {
166   // Search the groups which this constraint touchs
167   std::vector<Slvs_hGroup> aGroups;
168   findGroups(theConstraint, aGroups);
169
170   // Process the groups list
171   if (aGroups.size() == 0)
172   { // There are no groups applicable for this constraint => create new one
173     boost::shared_ptr<SketchPlugin_Sketch> aWP = findWorkplaneForConstraint(theConstraint);
174     if (!aWP) return false;
175     SketchSolver_ConstraintGroup aGroup(aWP);
176     aGroup.addConstraint(theConstraint);
177     myGroups.push_back(aGroup);
178     return true;
179   }
180   else if (aGroups.size() == 1)
181   { // Only one group => add constraint into it
182     Slvs_hGroup aGroupId = *(aGroups.begin());
183     std::vector<SketchSolver_ConstraintGroup>::iterator aGroupIter;
184     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
185       if (aGroupIter->getId() == aGroupId)
186         return aGroupIter->addConstraint(theConstraint);
187   }
188   else if (aGroups.size() > 1)
189   { // Several groups applicable for this constraint => need to merge them
190     /// \todo Implement merging of groups
191   }
192
193   // Something goes wrong
194   return false;
195 }
196
197
198 void SketchSolver_ConstraintManager::findGroups(
199               boost::shared_ptr<SketchPlugin_Constraint> theConstraint, 
200               std::vector<Slvs_hGroup>&                  theGroupIDs) const
201 {
202   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
203   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
204     if (aGroupIter->isInteract(theConstraint))
205       theGroupIDs.push_back(aGroupIter->getId());
206 }
207
208 boost::shared_ptr<SketchPlugin_Sketch> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
209               boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
210 {
211   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
212   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
213   {
214     boost::shared_ptr<SketchPlugin_Sketch> aWP = aGroupIter->getWorkplane();
215     boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = 
216       boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
217     std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
218     std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
219     for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
220       if (*anIter == theConstraint)
221         return aWP; // workplane is found
222   }
223
224   return boost::shared_ptr<SketchPlugin_Sketch>();
225 }
226
227
228
229 // ========================================================
230 // =========  SketchSolver_ConstraintGroup  ===============
231 // ========================================================
232
233 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
234   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Sketch> theWorkplane)
235   : myID(++myGroupIndexer),
236     myParamMaxID(0),
237     myEntityMaxID(0),
238     myConstrMaxID(0),
239     myConstraintMap()
240 {
241   myParams.clear();
242   myEntities.clear();
243   myConstraints.clear();
244
245   // Nullify all elements of the set of equations
246   myConstrSet.param = 0;
247   myConstrSet.entity = 0;
248   myConstrSet.constraint = 0;
249   myConstrSet.failed = 0;
250
251   // Initialize workplane
252   myWorkplane.h = 0;
253   addWorkplane(theWorkplane);
254 }
255
256 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
257 {
258   myParams.clear();
259   myEntities.clear();
260   myConstraints.clear();
261   myConstraintMap.clear();
262
263   if (myConstrSet.param)
264     delete [] myConstrSet.param;
265   if (myConstrSet.entity)
266     delete [] myConstrSet.entity;
267   if (myConstrSet.constraint)
268     delete [] myConstrSet.constraint;
269   if (myConstrSet.failed)
270     delete [] myConstrSet.failed;
271 }
272
273 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
274                 boost::shared_ptr<SketchPlugin_Sketch> theWorkplane) const
275 {
276   return theWorkplane == mySketch;
277 }
278
279 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
280                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
281 {
282   /// \todo Should be implemented
283   return false;
284 }
285
286 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addConstraint(
287                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
288 {
289   // There is no workplane yet, something wrong
290   if (myWorkplane.h == 0)
291     return false;
292
293   // Get constraint type and verify the constraint parameters are correct
294   int aConstrType = getConstraintType(theConstraint);
295   if (aConstrType == SLVS_C_UNKNOWN)
296     return false;
297
298   // Create constraint parameters
299   double aDistance = 0.0; // scalar value of the constraint
300   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
301     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
302   if (aDistAttr)
303     aDistance = aDistAttr->value();
304
305   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
306   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
307   {
308     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr = 
309       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
310         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
311       );
312     aConstrEnt[indAttr] = addEntity(aConstrAttr->attr());
313   }
314
315   // Create SolveSpace constraint structure
316   Slvs_Constraint aConstraint = 
317     Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h, 
318                         aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
319   myConstraints.push_back(aConstraint);
320   myConstraintMap[theConstraint] = *(myConstraints.rbegin());
321
322   return true;
323 }
324
325 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addEntity(
326                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
327 {
328   // Look over supported types of entities
329
330   // Point in 3D
331   boost::shared_ptr<GeomDataAPI_Point> aPoint = 
332     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
333   if (aPoint)
334   {
335     Slvs_hParam aX = addParameter(aPoint->x());
336     Slvs_hParam aY = addParameter(aPoint->y());
337     Slvs_hParam aZ = addParameter(aPoint->z());
338     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
339     myEntities.push_back(aPtEntity);
340     return aPtEntity.h;
341   }
342
343   // Point in 2D
344   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = 
345     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
346   if (aPoint2D)
347   {
348     // The 2D points are created on workplane. So, if there is no workplane yet, then error
349     if (myWorkplane.h == 0)
350       return 0;
351     Slvs_hParam aU = addParameter(aPoint2D->x());
352     Slvs_hParam aV = addParameter(aPoint2D->y());
353     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
354     myEntities.push_back(aPt2DEntity);
355     return aPt2DEntity.h;
356   }
357
358   /// \todo Other types of entities
359   
360   // Unsupported or wrong entity type
361   return 0;
362 }
363
364 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addNormal(
365                 boost::shared_ptr<ModelAPI_Attribute> theDirX, 
366                 boost::shared_ptr<ModelAPI_Attribute> theDirY)
367 {
368   // Convert axes to the coordinates of normal
369   std::vector<double> aNormCoord;
370   ConvertAttributeToParamList(aNormCoord, theDirX, theDirY);
371   
372   // Create a normal
373   Slvs_hParam aNormParams[4];
374   for (int i = 0; i < 4; i++)
375     aNormParams[i] = addParameter(aNormCoord[i]);
376   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID, 
377                 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
378   myEntities.push_back(aNormal);
379   return aNormal.h;
380 }
381
382
383 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
384                 boost::shared_ptr<SketchPlugin_Sketch> theSketch)
385 {
386   if (myWorkplane.h)
387     return false; // the workplane already exists
388
389   // Get parameters of workplane
390   boost::shared_ptr<ModelAPI_Attribute> aDirX    = theSketch->data()->attribute(SKETCH_ATTR_DIRX);
391   boost::shared_ptr<ModelAPI_Attribute> aDirY    = theSketch->data()->attribute(SKETCH_ATTR_DIRY);
392   boost::shared_ptr<ModelAPI_Attribute> anOrigin = theSketch->data()->attribute(SKETCH_ATTR_ORIGIN);
393   // Transform them into SolveSpace format
394   Slvs_hEntity aNormalWP = addNormal(aDirX, aDirY);
395   if (!aNormalWP) return false;
396   Slvs_hEntity anOriginWP = addEntity(anOrigin);
397   if (!anOriginWP) return false;
398   // Create workplane
399   myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
400   mySketch = theSketch;
401   // Workplane should be added to the list of entities
402   myEntities.push_back(myWorkplane);
403   return true;
404 }
405
406 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane(
407                 boost::shared_ptr<SketchPlugin_Sketch> theSketch)
408 {
409   // Renew Sketch pointer
410   mySketch = theSketch;
411
412   // Get parameters of workplane
413   boost::shared_ptr<ModelAPI_Attribute> aDirX  = theSketch->data()->attribute(SKETCH_ATTR_DIRX);
414   boost::shared_ptr<ModelAPI_Attribute> aDirY  = theSketch->data()->attribute(SKETCH_ATTR_DIRY);
415   boost::shared_ptr<ModelAPI_Attribute> anOrig = theSketch->data()->attribute(SKETCH_ATTR_ORIGIN);
416   // Transform them to lists of coordinates
417   std::vector<double> aNormal;
418   ConvertAttributeToParamList(aNormal, aDirX, aDirY);
419   std::vector<double> anOrigin;
420   ConvertAttributeToParamList(anOrigin, anOrig);
421
422   // Search the normal and the origin in the parameters list and update their values.
423   // Remark: entities are sorted in the vector, so the most probable position 
424   //         of the entity is equal to identifier the entity
425   
426   // search normal
427   int aEntPos = Search(myWorkplane.normal, myEntities);
428   if (aEntPos < 0) return false;
429   // search first parameter of normal
430   int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
431   if (aParamPos < 0) return false;
432   std::vector<Slvs_Param>::iterator aParamIter = myParams.begin() + aParamPos;
433   // change normal parameters
434   std::vector<double>::iterator anIter;
435   for (anIter = aNormal.begin(); anIter != aNormal.end(); anIter++, aParamIter++)
436     aParamIter->val = *anIter;
437
438   // search origin
439   aEntPos = Search(myWorkplane.point[0], myEntities);
440   if (aEntPos < 0) return false;
441   // search first parameter of origin
442   aParamPos = Search(myEntities[aEntPos].param[0], myParams);
443   if (aParamPos < 0) return false;
444   aParamIter = myParams.begin() + aParamPos;
445   // change origin's parameters
446   for (anIter = anOrigin.begin(); anIter != anOrigin.end(); anIter++, aParamIter++)
447     aParamIter->val = *anIter;
448
449   return true;
450 }
451
452
453 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addParameter(double theParam)
454 {
455   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
456   myParams.push_back(aParam);
457   return aParam.h;
458 }
459
460 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
461                 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
462 {
463   // Constraint for coincidence of two points
464   boost::shared_ptr<SketchPlugin_ConstraintCoincidence> aPtEquiv = 
465     boost::dynamic_pointer_cast<SketchPlugin_ConstraintCoincidence>(theConstraint);
466   if (aPtEquiv)
467   {
468     // Verify the constraint has only two attributes and they are points
469     int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point 
470     int aPt3d = 0; // bit-mapped field, the same information for 3D points
471     for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
472     {
473       boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr = 
474         boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
475           theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
476         );
477       // Verify the attribute is a 2D point
478       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = 
479         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
480       if (aPoint2D)
481       {
482         aPt2d |= (1 << indAttr);
483         continue;
484       }
485       // Verify the attribute is a 3D point
486       boost::shared_ptr<GeomDataAPI_Point> aPoint3D = 
487         boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
488       if (aPoint3D)
489       {
490         aPt3d |= (1 << indAttr);
491         continue;
492       }
493       // Attribute neither 2D nor 3D point is not supported by this type of constraint
494       return SLVS_C_UNKNOWN;
495     }
496     // The constrained points should be in first and second positions,
497     // so the expected value of aPt2d or aPt3d is 3
498     if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
499       return SLVS_C_POINTS_COINCIDENT;
500     // Constraint parameters are wrong
501     return SLVS_C_UNKNOWN;
502   }
503
504   /// \todo Implement other kind of constrtaints
505
506   return SLVS_C_UNKNOWN;
507 }
508
509
510
511 // ========================================================
512 // =========      Auxiliary functions       ===============
513 // ========================================================
514
515 void ConvertAttributeToParamList(
516                 std::vector<double>&                  theParams, 
517                 boost::shared_ptr<ModelAPI_Attribute> theAttr, 
518                 boost::shared_ptr<ModelAPI_Attribute> theNormExtraAttr)
519 {
520   // Point in 3D
521   boost::shared_ptr<GeomDataAPI_Point> aPoint = 
522     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttr);
523   if (aPoint)
524   {
525     theParams.push_back(aPoint->x());
526     theParams.push_back(aPoint->y());
527     theParams.push_back(aPoint->z());
528     return ;
529   }
530
531   // Point in 2D
532   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = 
533     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttr);
534   if (aPoint2D)
535   {
536     theParams.push_back(aPoint2D->x());
537     theParams.push_back(aPoint2D->y());
538     return ;
539   }
540
541   // Normal in 3D
542   boost::shared_ptr<GeomDataAPI_Dir> aDirX = 
543     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theAttr);
544   boost::shared_ptr<GeomDataAPI_Dir> aDirY = 
545     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theNormExtraAttr);
546   if (aDirX && aDirY)
547   {
548     // quaternion parameters of normal vector
549     double qw, qx, qy, qz;
550     Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), 
551                         aDirY->x(), aDirY->y(), aDirY->z(), 
552                         &qw, &qx, &qy, &qz);
553     theParams.push_back(qw);
554     theParams.push_back(qx);
555     theParams.push_back(qy);
556     theParams.push_back(qz);
557   }
558
559   /// \todo Other types of entities
560 }
561
562 template <typename T>
563 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
564 {
565   std::vector<T>::const_iterator aEntIter = theEntities.begin() + theEntityID - 1;
566   while (aEntIter != theEntities.end() && aEntIter->h > theEntityID)
567     aEntIter--;
568   while (aEntIter != theEntities.end() && aEntIter->h < theEntityID)
569     aEntIter++;
570   if (aEntIter == theEntities.end())
571     return -1;
572   return aEntIter - theEntities.begin();
573 }
574