Salome HOME
Merge branch 'MessagesGroups'
[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
16 #include <SketchPlugin_Constraint.h>
17 #include <SketchPlugin_ConstraintCoincidence.h>
18 #include <SketchPlugin_Line.h>
19 #include <SketchPlugin_Point.h>
20 #include <SketchPlugin_Sketch.h>
21
22 #include <math.h>
23 #include <assert.h>
24
25 #include <set>
26
27 /// Tolerance for value of parameters
28 const double tolerance = 1.e-10;
29
30 // Initialization of constraint manager self pointer
31 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
32
33 /// Global constraint manager object
34 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
35
36 /// This value is used to give unique index to the groups
37 static Slvs_hGroup myGroupIndexer = 0;
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 // ========================================================
50 // ========= SketchSolver_ConstraintManager ===============
51 // ========================================================
52 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
53 {
54   if (!_self)
55     _self = new SketchSolver_ConstraintManager();
56   return _self;
57 }
58
59 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
60 {
61   myGroups.clear();
62
63   // Register in event loop
64   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
65   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
66   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
67   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_MOVED));
68 }
69
70 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
71 {
72   myGroups.clear();
73 }
74
75 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
76 {
77   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED) ||
78       theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED) || 
79       theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED))
80   {
81     const Model_FeatureUpdatedMessage* anUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
82     std::set< boost::shared_ptr<ModelAPI_Feature> > aFeatures = anUpdateMsg->features();
83
84     bool isModifiedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED);
85     if (!isModifiedEvt)
86     {
87       std::set< boost::shared_ptr<ModelAPI_Feature> >::iterator aFeatIter;
88       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
89       {
90         // Only sketches and constraints can be added by Create event
91         const std::string& aFeatureKind = (*aFeatIter)->getKind();
92         if (aFeatureKind.compare("Sketch") == 0)
93         {
94           boost::shared_ptr<SketchPlugin_Feature> aSketch =
95             boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
96           if (aSketch)
97             changeWorkplane(aSketch);
98           return ;
99         }
100         boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
101           boost::dynamic_pointer_cast<SketchPlugin_Constraint>(*aFeatIter);
102         if (aConstraint)
103           changeConstraint(aConstraint);
104         else
105         {
106           // Sketch plugin features can be only updated
107           boost::shared_ptr<SketchPlugin_Feature> aFeature =
108             boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
109           if (aFeature)
110             updateEntity(aFeature);
111         }
112       }
113     }
114
115     // Solve the set of constraints
116     resolveConstraints();
117   }
118   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
119   {
120     const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
121     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
122
123     // Find "Sketch" in groups. The constraint groups should be updated when an object removed from Sketch
124     std::set<std::string>::const_iterator aFGrIter;
125     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
126       if (aFGrIter->compare("Sketch") == 0)
127         break;
128     
129     if (aFGrIter != aFeatureGroups.end())
130     {
131       std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
132       while (aGroupIter != myGroups.end())
133       {
134         if ((*aGroupIter)->updateGroup())
135         { // the group should be removed
136           delete *aGroupIter;
137           int aShift = aGroupIter - myGroups.begin();
138           myGroups.erase(aGroupIter);
139           aGroupIter = myGroups.begin() + aShift;
140         }
141         else aGroupIter++;
142       }
143     }
144   }
145 }
146
147 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
148 {
149   bool aResult = true; // changed when a workplane wrongly updated
150   bool isUpdated = false;
151   // Try to update specified workplane in all groups
152   std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
153   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
154     if ((*aGroupIter)->isBaseWorkplane(theSketch))
155     {
156       isUpdated = true;
157       if (!(*aGroupIter)->updateWorkplane())
158         aResult = false;
159     }
160   // If the workplane is not updated, so this is a new workplane
161   if (!isUpdated)
162   {
163     SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
164     // Verify that the group is created successfully
165     if (!aNewGroup->isBaseWorkplane(theSketch))
166     {
167       delete aNewGroup;
168       return false;
169     }
170     myGroups.push_back(aNewGroup);
171   }
172   return aResult;
173 }
174
175 bool SketchSolver_ConstraintManager::changeConstraint(
176               boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
177 {
178   // Search the groups which this constraint touchs
179   std::vector<Slvs_hGroup> aGroups;
180   findGroups(theConstraint, aGroups);
181
182   // Process the groups list
183   if (aGroups.size() == 0)
184   { // There are no groups applicable for this constraint => create new one
185     boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
186     if (!aWP) return false;
187     SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
188     if (!aGroup->changeConstraint(theConstraint))
189     {
190       delete aGroup;
191       return false;
192     }
193     myGroups.push_back(aGroup);
194     return true;
195   }
196   else if (aGroups.size() == 1)
197   { // Only one group => add constraint into it
198     Slvs_hGroup aGroupId = *(aGroups.begin());
199     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
200     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
201       if ((*aGroupIter)->getId() == aGroupId)
202         return (*aGroupIter)->changeConstraint(theConstraint);
203   }
204   else if (aGroups.size() > 1)
205   { // Several groups applicable for this constraint => need to merge them
206     /// \todo Implement merging of groups
207   }
208
209   // Something goes wrong
210   return false;
211 }
212
213 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
214 {
215   // Create list of attributes depending on type of the feature
216   std::vector<std::string> anAttrList;
217   const std::string& aFeatureKind = theFeature->getKind();
218   // Point
219   if (aFeatureKind.compare("SketchPoint") == 0)
220     anAttrList.push_back(POINT_ATTR_COORD);
221   // Line
222   else if (aFeatureKind.compare("SketchLine") == 0)
223   {
224     anAttrList.push_back(LINE_ATTR_START);
225     anAttrList.push_back(LINE_ATTR_END);
226   }
227   /// \todo Other types of features should be implemented
228
229   // Check changing of feature's attributes (go through the groups and search usage of the attributes)
230   std::vector<std::string>::const_iterator anAttrIter;
231   for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
232   {
233     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
234     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
235     {
236       boost::shared_ptr<ModelAPI_Attribute> anAttribute =
237         boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
238       (*aGroupIter)->updateEntityIfPossible(anAttribute);
239     }
240   }
241 }
242
243
244 void SketchSolver_ConstraintManager::findGroups(
245               boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
246               std::vector<Slvs_hGroup>&                  theGroupIDs) const
247 {
248   boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
249
250   std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
251   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
252     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
253       theGroupIDs.push_back((*aGroupIter)->getId());
254 }
255
256 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
257               boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
258 {
259   // Already verified workplanes
260   std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
261
262   std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
263   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
264   {
265     boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
266     if (aVerified.find(aWP) != aVerified.end())
267       continue;
268
269     boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
270       boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
271     std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
272     std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
273     for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
274       if (*anIter == theConstraint)
275         return aWP; // workplane is found
276     aVerified.insert(aWP);
277   }
278
279   return boost::shared_ptr<SketchPlugin_Feature>();
280 }
281
282 void SketchSolver_ConstraintManager::resolveConstraints()
283 {
284   std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
285   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
286     (*aGroupIter)->resolveConstraints();
287
288   // Features may be updated => send events
289   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
290 }
291
292
293
294 // ========================================================
295 // =========  SketchSolver_ConstraintGroup  ===============
296 // ========================================================
297
298 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
299   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
300   : myID(++myGroupIndexer),
301     myParamMaxID(0),
302     myEntityMaxID(0),
303     myConstrMaxID(0),
304     myConstraintMap(),
305     myNeedToSolve(false),
306     myConstrSolver()
307 {
308   myParams.clear();
309   myEntities.clear();
310   myConstraints.clear();
311
312   // Initialize workplane
313   myWorkplane.h = SLVS_E_UNKNOWN;
314   assert(addWorkplane(theWorkplane));
315 }
316
317 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
318 {
319   myParams.clear();
320   myEntities.clear();
321   myConstraints.clear();
322   myConstraintMap.clear();
323
324   // If the group with maximal identifier is deleted, decrease the indexer
325   if (myID == myGroupIndexer)
326     myGroupIndexer--;
327 }
328
329 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
330                 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
331 {
332   return theWorkplane == mySketch;
333 }
334
335 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
336                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
337 {
338   // Check the group is empty
339   if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
340     return true;
341
342   // Go through constraint entities and verify if some of them already in the group
343   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
344   {
345     boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
346       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
347         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
348       );
349     if (!aCAttrRef) continue;
350     if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
351       return true;
352   }
353
354   // Entities did not found
355   return false;
356 }
357
358 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeConstraint(
359                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
360 {
361   // There is no workplane yet, something wrong
362   if (myWorkplane.h == SLVS_E_UNKNOWN)
363     return false;
364
365   // Search this constraint in the current group to update it
366   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
367     aConstrMapIter = myConstraintMap.find(theConstraint);
368   std::vector<Slvs_Constraint>::iterator aConstrIter;
369   if (aConstrMapIter != myConstraintMap.end())
370   {
371     int aConstrPos = Search(aConstrMapIter->second, myConstraints);
372     aConstrIter = myConstraints.begin() + aConstrPos;
373   }
374
375   // Get constraint type and verify the constraint parameters are correct
376   int aConstrType = getConstraintType(theConstraint);
377   if (aConstrType == SLVS_C_UNKNOWN ||
378      (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
379     return false;
380
381   // Create constraint parameters
382   double aDistance = 0.0; // scalar value of the constraint
383   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
384     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
385   if (aDistAttr)
386   {
387     aDistance = aDistAttr->value();
388     if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
389     {
390       myNeedToSolve = true;
391       aConstrIter->valA = aDistance;
392     }
393   }
394
395   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
396   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
397   {
398     aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
399     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
400       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
401         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
402       );
403     if (!aConstrAttr) continue;
404     aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
405   }
406
407   if (aConstrMapIter == myConstraintMap.end())
408   {
409     // Create SolveSpace constraint structure
410     Slvs_Constraint aConstraint =
411       Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
412                           aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
413     myConstraints.push_back(aConstraint);
414     myConstraintMap[theConstraint] = aConstraint.h;
415   }
416   return true;
417 }
418
419 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeEntity(
420                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
421 {
422   // If the entity is already in the group, try to find it
423   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
424     aEntIter = myEntityMap.find(theEntity);
425   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
426   if (aEntIter == myEntityMap.end()) // no such entity => should be created
427     aParamIter = myParams.end();
428   else
429   { // the entity already exists
430     int aEntPos = Search(aEntIter->second, myEntities);
431     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
432     aParamIter = myParams.begin() + aParamPos;
433   }
434
435   // Look over supported types of entities
436
437   // Point in 3D
438   boost::shared_ptr<GeomDataAPI_Point> aPoint =
439     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
440   if (aPoint)
441   {
442     Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
443     Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
444     Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
445
446     if (aEntIter != myEntityMap.end()) // the entity already exists
447       return aEntIter->second;
448
449     // New entity
450     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
451     myEntities.push_back(aPtEntity);
452     myEntityMap[theEntity] = aPtEntity.h;
453     return aPtEntity.h;
454   }
455
456   // Point in 2D
457   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
458     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
459   if (aPoint2D)
460   {
461     // The 2D points are created on workplane. So, if there is no workplane yet, then error
462     if (myWorkplane.h == SLVS_E_UNKNOWN)
463       return SLVS_E_UNKNOWN;
464     Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
465     Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
466
467     if (aEntIter != myEntityMap.end()) // the entity already exists
468       return aEntIter->second;
469
470     // New entity
471     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
472     myEntities.push_back(aPt2DEntity);
473     myEntityMap[theEntity] = aPt2DEntity.h;
474     return aPt2DEntity.h;
475   }
476
477   /// \todo Other types of entities
478
479   // Unsupported or wrong entity type
480   return SLVS_E_UNKNOWN;
481 }
482
483 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeNormal(
484                 boost::shared_ptr<ModelAPI_Attribute> theDirX,
485                 boost::shared_ptr<ModelAPI_Attribute> theDirY,
486                 boost::shared_ptr<ModelAPI_Attribute> theNorm)
487 {
488   boost::shared_ptr<GeomDataAPI_Dir> aDirX =
489     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
490   boost::shared_ptr<GeomDataAPI_Dir> aDirY =
491     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
492   if (!aDirX || !aDirY ||
493      (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
494      (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
495     return SLVS_E_UNKNOWN;
496
497   // quaternion parameters of normal vector
498   double qw, qx, qy, qz;
499   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
500                       aDirY->x(), aDirY->y(), aDirY->z(),
501                       &qw, &qx, &qy, &qz);
502   double aNormCoord[4] = {qw, qx, qy, qz};
503
504   // Try to find existent normal
505   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
506     aEntIter = myEntityMap.find(theNorm);
507   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
508   if (aEntIter == myEntityMap.end()) // no such entity => should be created
509     aParamIter = myParams.end();
510   else
511   { // the entity already exists, update it
512     int aEntPos = Search(aEntIter->second, myEntities);
513     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
514     aParamIter = myParams.begin() + aParamPos;
515   }
516
517   // Change parameters of the normal
518   Slvs_hParam aNormParams[4];
519   for (int i = 0; i < 4; i++)
520     aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
521
522   if (aEntIter != myEntityMap.end()) // the entity already exists
523     return aEntIter->second;
524
525   // Create a normal
526   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
527                 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
528   myEntities.push_back(aNormal);
529   myEntityMap[theNorm] = aNormal.h;
530   return aNormal.h;
531 }
532
533
534 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
535                 boost::shared_ptr<SketchPlugin_Feature> theSketch)
536 {
537   if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
538     return false; // the workplane already exists or the function parameter is not Sketch
539
540   mySketch = theSketch;
541   updateWorkplane();
542   return true;
543 }
544
545 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane()
546 {
547   // Get parameters of workplane
548   boost::shared_ptr<ModelAPI_Attribute> aDirX    = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
549   boost::shared_ptr<ModelAPI_Attribute> aDirY    = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
550   boost::shared_ptr<ModelAPI_Attribute> aNorm    = mySketch->data()->attribute(SKETCH_ATTR_NORM);
551   boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
552   // Transform them into SolveSpace format
553   Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
554   if (!aNormalWP) return false;
555   Slvs_hEntity anOriginWP = changeEntity(anOrigin);
556   if (!anOriginWP) return false;
557
558   if (!myWorkplane.h)
559   {
560     // Create workplane
561     myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
562     // Workplane should be added to the list of entities
563     myEntities.push_back(myWorkplane);
564   }
565   return true;
566 }
567
568
569 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeParameter(
570                 const double&                            theParam,
571                 std::vector<Slvs_Param>::const_iterator& thePrmIter)
572 {
573   if (thePrmIter != myParams.end())
574   { // Parameter should be updated
575     int aParamPos = thePrmIter - myParams.begin();
576     if (fabs(thePrmIter->val - theParam) > tolerance)
577     {
578       myNeedToSolve = true; // parameter is changed, need to resolve constraints
579       myParams[aParamPos].val = theParam;
580     }
581     thePrmIter++;
582     return myParams[aParamPos].h;
583   }
584
585   // Newly created parameter
586   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
587   myParams.push_back(aParam);
588   myNeedToSolve = true;
589   // The list of parameters is changed, move iterator to the end of the list to avoid problems
590   thePrmIter = myParams.end();
591   return aParam.h;
592 }
593
594 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
595                 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
596 {
597   const std::string& aConstraintKind = theConstraint->getKind();
598   // Constraint for coincidence of two points
599   if (aConstraintKind.compare("SketchConstraintCoincidence") == 0)
600   {
601     // Verify the constraint has only two attributes and they are points
602     int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point
603     int aPt3d = 0; // bit-mapped field, the same information for 3D points
604     for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
605     {
606       boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr =
607         boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
608           theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
609         );
610       if (!anAttr) continue;
611       // Verify the attribute is a 2D point
612       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
613         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
614       if (aPoint2D)
615       {
616         aPt2d |= (1 << indAttr);
617         continue;
618       }
619       // Verify the attribute is a 3D point
620       boost::shared_ptr<GeomDataAPI_Point> aPoint3D =
621         boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
622       if (aPoint3D)
623       {
624         aPt3d |= (1 << indAttr);
625         continue;
626       }
627       // Attribute neither 2D nor 3D point is not supported by this type of constraint
628       return SLVS_C_UNKNOWN;
629     }
630     // The constrained points should be in first and second positions,
631     // so the expected value of aPt2d or aPt3d is 3
632     if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
633       return SLVS_C_POINTS_COINCIDENT;
634     // Constraint parameters are wrong
635     return SLVS_C_UNKNOWN;
636   }
637
638   /// \todo Implement other kind of constrtaints
639
640   return SLVS_C_UNKNOWN;
641 }
642
643 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::resolveConstraints()
644 {
645   if (!myNeedToSolve)
646     return;
647
648   myConstrSolver.setGroupID(myID);
649   myConstrSolver.setParameters(myParams);
650   myConstrSolver.setEntities(myEntities);
651   myConstrSolver.setConstraints(myConstraints);
652
653   if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
654   { // solution succeeded, store results into correspondent attributes
655     // Obtain result into the same list of parameters
656     if (!myConstrSolver.getResult(myParams))
657       return;
658
659     std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
660       anEntIter = myEntityMap.begin();
661     for ( ; anEntIter != myEntityMap.end(); anEntIter++)
662       updateAttribute(anEntIter->first, anEntIter->second);
663   }
664   /// \todo Implement error handling
665
666   removeTemporaryConstraints();
667   myNeedToSolve = false;
668 }
669
670 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateGroup()
671 {
672   // Check for valid sketch
673   if (!mySketch->data()->isValid())
674     return true;
675
676   // Fast check for constraint validity. If all constraints are valid, no need to update the group
677   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
678     aConstrIter = myConstraintMap.rbegin();
679   bool isAllValid = true;
680   for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
681     if (!aConstrIter->first->data()->isValid())
682       isAllValid = false;
683   if (isAllValid)
684     return false;
685
686   // Remove invalid constraints.
687   // There only constraint will be deleted (parameters and entities) will be removed below
688   std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
689   std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
690   for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
691   {
692     bool isValid = aConstrIter->first->data()->isValid();
693
694     int aConstrPos = Search(aConstrIter->second, myConstraints);
695     if (aConstrPos < (int)myConstraints.size())
696     {
697       Slvs_hEntity aConstrEnt[] = {
698         myConstraints[aConstrPos].ptA,     myConstraints[aConstrPos].ptB,
699         myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
700       for (int i = 0; i < 4; i++)
701         if (aConstrEnt[i] != SLVS_E_UNKNOWN)
702         {
703           if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
704             anEntToDelete[aConstrEnt[i]] = !isValid;
705           else if (isValid) // constraint is valid => no need to remove its entities
706             anEntToDelete[aConstrEnt[i]] = false;
707         }
708       if (!isValid)
709       {
710         myConstraints.erase(myConstraints.begin() + aConstrPos);
711         if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
712           myConstrMaxID--;
713         aConstrToDelete.push_front(aConstrIter->first);
714       }
715     }
716   }
717   std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
718   for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
719     myConstraintMap.erase(*aDelIter);
720
721   // Remove invalid and unused entities
722   std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
723   for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
724     if (aEDelIter->second)
725     {
726       int anEntPos = Search(aEDelIter->first, myEntities);
727       std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
728       // Number of parameters for the entity
729       int aNbParams = 0;
730       while (aEntIter->param[aNbParams]) aNbParams++;
731       if (aNbParams == 0) continue;
732       // Decrease parameter indexer if there are deleted parameter with higher IDs
733       if (aEntIter->param[aNbParams-1] == myParamMaxID)
734         myParamMaxID -= aNbParams;
735       // Remove parameters of the entity
736       int aParamPos = Search(aEntIter->param[0], myParams);
737       myParams.erase(myParams.begin() + aParamPos,
738                      myParams.begin() + aParamPos + aNbParams);
739
740       // Remove entity
741       if (aEDelIter->first == myEntityMaxID)
742         myEntityMaxID--;
743       myEntities.erase(myEntities.begin() + anEntPos);
744       // Remove such entity from myEntityMap
745       std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
746         anEntMapIter = myEntityMap.begin();
747       for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
748         if (anEntMapIter->second == aEDelIter->first)
749           break;
750       if (anEntMapIter != myEntityMap.end())
751         myEntityMap.erase(anEntMapIter);
752     }
753
754   return false;
755 }
756
757 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateAttribute(
758                 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
759                 const Slvs_hEntity&                   theEntityID)
760 {
761   // Search the position of the first parameter of the entity
762   int anEntPos = Search(theEntityID, myEntities);
763   int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
764
765   // Look over supported types of entities
766
767   // Point in 3D
768   boost::shared_ptr<GeomDataAPI_Point> aPoint =
769     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
770   if (aPoint)
771   {
772     aPoint->setValue(myParams[aFirstParamPos].val,
773                      myParams[aFirstParamPos+1].val,
774                      myParams[aFirstParamPos+2].val);
775     return ;
776   }
777
778   // Point in 2D
779   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
780     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
781   if (aPoint2D)
782   {
783     aPoint2D->setValue(myParams[aFirstParamPos].val,
784                        myParams[aFirstParamPos+1].val);
785     return ;
786   }
787
788   /// \todo Support other types of entities
789 }
790
791 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateEntityIfPossible(
792                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
793 {
794   if (myEntityMap.find(theEntity) != myEntityMap.end())
795   {
796     // If the attribute is a point and it is changed (the group needs to rebuild),
797     // probably user has dragged this point into this position,
798     // so it is necessary to add constraint which will guarantee the point will not change
799
800     // Store myNeedToSolve flag to verify the entity is really changed
801     bool aNeedToSolveCopy = myNeedToSolve;
802     myNeedToSolve = false;
803
804     changeEntity(theEntity);
805
806     if (myNeedToSolve) // the entity is changed
807     {
808       // Verify the entity is a point and add temporary constraint of permanency
809       boost::shared_ptr<GeomDataAPI_Point> aPoint =
810         boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
811       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
812         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
813       if (aPoint || aPoint2D)
814         addTemporaryConstraintWhereDragged(theEntity);
815     }
816
817     // Restore flag of changes
818     myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
819   }
820 }
821
822 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
823                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
824 {
825   // Find identifier of the entity
826   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
827     anEntIter = myEntityMap.find(theEntity);
828
829   // Create WHERE_DRAGGED constraint
830   Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
831                                                   myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
832   myConstraints.push_back(aWDConstr);
833   myTempConstraints.push_back(aWDConstr.h);
834 }
835
836 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::removeTemporaryConstraints()
837 {
838   std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
839   for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
840   {
841     int aConstrPos = Search(*aTmpConstrIter, myConstraints);
842     myConstraints.erase(myConstraints.begin() + aConstrPos);
843
844     // If the removing constraint has higher index, decrease the indexer
845     if (*aTmpConstrIter == myConstrMaxID)
846       myConstrMaxID--;
847   }
848   myTempConstraints.clear();
849 }
850
851
852
853 // ========================================================
854 // =========      Auxiliary functions       ===============
855 // ========================================================
856
857 template <typename T>
858 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
859 {
860   int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
861   int aVecSize = theEntities.size();
862   while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
863     aResIndex--;
864   while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
865     aResIndex++;
866   if (aResIndex == -1)
867     aResIndex = aVecSize;
868   return aResIndex;
869 }