]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintManager.cpp
Salome HOME
Merge remote-tracking branch 'remotes/origin/master' into SolveSpace
[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 // ========================================================
29 // ========= SketchSolver_ConstraintManager ===============
30 // ========================================================
31 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
32 {
33   if (!_self)
34     _self = new SketchSolver_ConstraintManager();
35   return _self;
36 }
37
38 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
39 {
40   myGroups.clear();
41
42   // Register in event loop
43   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
44   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
45   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
46 }
47
48 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
49 {
50   myGroups.clear();
51 }
52
53 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
54 {
55   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED))
56   {
57     const Model_FeatureUpdatedMessage* aCreateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
58
59     // Only sketches and constraints can be added by Create event
60     boost::shared_ptr<SketchPlugin_Sketch> aSketch = 
61       boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aCreateMsg->feature());
62     if (aSketch)
63     {
64       addWorkplane(aSketch);
65       return ;
66     }
67     boost::shared_ptr<SketchPlugin_Constraint> aConstraint = 
68       boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aCreateMsg->feature());
69     if (aConstraint)
70     {
71       addConstraint(aConstraint);
72       return ;
73     }
74   }
75   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
76   {
77     const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
78     /// \todo Implement deleting objects on event
79   }
80   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED))
81   {
82     const Model_FeatureUpdatedMessage* aUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
83
84     boost::shared_ptr<SketchPlugin_Sketch> aSketch = 
85       boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aUpdateMsg->feature());
86     if (aSketch)
87     {
88 //      updateWorkplane(aSketch);
89       return ;
90     }
91
92     boost::shared_ptr<SketchPlugin_Constraint> aConstraint = 
93       boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aUpdateMsg->feature());
94     if (aConstraint)
95     {
96 //      updateConstraint(aConstraint);
97       return ;
98     }
99
100     boost::shared_ptr<SketchPlugin_Feature> aFeature = 
101       boost::dynamic_pointer_cast<SketchPlugin_Feature>(aUpdateMsg->feature());
102 //    if (aFeature)
103 //      updateEntity(aFeature);
104   }
105 }
106
107
108 bool SketchSolver_ConstraintManager::addWorkplane(boost::shared_ptr<SketchPlugin_Sketch> theSketch)
109 {
110   // Check the specified workplane is already used
111   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
112   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
113     if (aGroupIter->isBaseWorkplane(theSketch))
114       return true;
115   // Create new group for workplane
116   SketchSolver_ConstraintGroup aNewGroup(theSketch);
117   // Verify that the group is created successfully
118   if (!aNewGroup.isBaseWorkplane(theSketch))
119     return false;
120   myGroups.push_back(aNewGroup);
121   return true;
122 }
123
124 bool SketchSolver_ConstraintManager::addConstraint(
125               boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
126 {
127   // Search the groups which this constraint touchs
128   std::vector<Slvs_hGroup> aGroups;
129   findGroups(theConstraint, aGroups);
130
131   // Process the groups list
132   if (aGroups.size() == 0)
133   { // There are no groups applicable for this constraint => create new one
134     SketchSolver_ConstraintGroup aGroup(findWorkplaneForConstraint(theConstraint));
135     aGroup.addConstraint(theConstraint);
136     myGroups.push_back(aGroup);
137   }
138   else if (aGroups.size() == 1)
139   { // Only one group => add constraint into it
140     Slvs_hGroup aGroupId = *(aGroups.begin());
141     std::vector<SketchSolver_ConstraintGroup>::iterator aGroupIter;
142     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
143       if (aGroupIter->getId() == aGroupId)
144         return aGroupIter->addConstraint(theConstraint);
145   }
146   else if (aGroups.size() > 1)
147   { // Several groups applicable for this constraint => need to merge them
148     /// \todo Implement merging of groups
149   }
150
151   // Something goes wrong
152   return false;
153 }
154
155
156 void SketchSolver_ConstraintManager::findGroups(
157               boost::shared_ptr<SketchPlugin_Constraint> theConstraint, 
158               std::vector<Slvs_hGroup>&                  theGroupIDs) const
159 {
160   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
161   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
162     if (aGroupIter->isInteract(theConstraint))
163       theGroupIDs.push_back(aGroupIter->getId());
164 }
165
166 boost::shared_ptr<SketchPlugin_Sketch> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
167               boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
168 {
169   std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
170   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
171   {
172     boost::shared_ptr<SketchPlugin_Sketch> aWP = aGroupIter->getWorkplane();
173     boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = 
174       boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
175     std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
176     std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
177     for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
178       if (*anIter == theConstraint)
179         return aWP; // workplane is found
180   }
181
182   return boost::shared_ptr<SketchPlugin_Sketch>();
183 }
184
185
186
187 // ========================================================
188 // =========  SketchSolver_ConstraintGroup  ===============
189 // ========================================================
190
191 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
192   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Sketch> theWorkplane)
193   : myID(++myGroupIndexer),
194     myParamMaxID(0),
195     myEntityMaxID(0),
196     myConstrMaxID(0),
197     myConstraintMap()
198 {
199   myParams.clear();
200   myEntities.clear();
201   myConstraints.clear();
202
203   // Nullify all elements of the set of equations
204   myConstrSet.param = 0;
205   myConstrSet.entity = 0;
206   myConstrSet.constraint = 0;
207   myConstrSet.failed = 0;
208
209   // Initialize workplane
210   myWorkplane.h = 0;
211   addWorkplane(theWorkplane);
212 }
213
214 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
215 {
216   myParams.clear();
217   myEntities.clear();
218   myConstraints.clear();
219   myConstraintMap.clear();
220
221   if (myConstrSet.param)
222     delete [] myConstrSet.param;
223   if (myConstrSet.entity)
224     delete [] myConstrSet.entity;
225   if (myConstrSet.constraint)
226     delete [] myConstrSet.constraint;
227   if (myConstrSet.failed)
228     delete [] myConstrSet.failed;
229 }
230
231 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
232                 boost::shared_ptr<SketchPlugin_Sketch> theWorkplane) const
233 {
234   return theWorkplane == mySketch;
235 }
236
237 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
238                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
239 {
240   /// \todo Should be implemented
241   return false;
242 }
243
244 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addConstraint(
245                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
246 {
247   // There is no workplane yet, something wrong
248   if (myWorkplane.h == 0)
249     return false;
250
251   // Get constraint type and verify the constraint parameters are correct
252   int aConstrType = getConstraintType(theConstraint);
253   if (aConstrType == SLVS_C_UNKNOWN)
254     return false;
255
256   // Create constraint parameters
257   double aDistance = 0.0; // scalar value of the constraint
258   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
259     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
260   if (aDistAttr)
261     aDistance = aDistAttr->value();
262
263   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
264   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
265   {
266     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr = 
267       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
268         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
269       );
270     aConstrEnt[indAttr] = addEntity(aConstrAttr->attr());
271   }
272
273   // Create SolveSpace constraint structure
274   Slvs_Constraint aConstraint = 
275     Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h, 
276                         aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
277   myConstraints.push_back(aConstraint);
278   myConstraintMap[theConstraint] = *(myConstraints.rbegin());
279
280   return true;
281 }
282
283 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addEntity(
284                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
285 {
286   // Look over supported types of entities
287
288   // Point in 3D
289   boost::shared_ptr<GeomDataAPI_Point> aPoint = 
290     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
291   if (aPoint)
292   {
293     Slvs_hParam aX = addParameter(aPoint->x());
294     Slvs_hParam aY = addParameter(aPoint->y());
295     Slvs_hParam aZ = addParameter(aPoint->z());
296     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
297     myEntities.push_back(aPtEntity);
298     return aPtEntity.h;
299   }
300
301   // Point in 2D
302   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = 
303     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
304   if (aPoint2D)
305   {
306     // The 2D points are created on workplane. So, if there is no workplane yet, then error
307     if (myWorkplane.h == 0)
308       return 0;
309     Slvs_hParam aU = addParameter(aPoint2D->x());
310     Slvs_hParam aV = addParameter(aPoint2D->y());
311     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
312     myEntities.push_back(aPt2DEntity);
313     return aPt2DEntity.h;
314   }
315
316   /// \todo Other types of entities
317   
318   // Unsupported or wrong entity type
319   return 0;
320 }
321
322 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addNormal(
323                 boost::shared_ptr<ModelAPI_Attribute> theDirX, 
324                 boost::shared_ptr<ModelAPI_Attribute> theDirY)
325 {
326   boost::shared_ptr<GeomDataAPI_Dir> aDirX = boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
327   boost::shared_ptr<GeomDataAPI_Dir> aDirY = boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
328   if (!aDirX || !aDirY)
329     return 0;
330
331   // quaternion parameters of normal vector
332   double qw, qx, qy, qz;
333   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), 
334                       aDirY->x(), aDirY->y(), aDirY->z(), 
335                       &qw, &qx, &qy, &qz);
336
337   // Create a normal
338   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID, 
339                       addParameter(qw), addParameter(qx), addParameter(qy), addParameter(qz));
340   myEntities.push_back(aNormal);
341   return aNormal.h;
342 }
343
344
345 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
346                 boost::shared_ptr<SketchPlugin_Sketch> theSketch)
347 {
348   if (myWorkplane.h)
349     return false; // the workplane already exists
350
351   // Get parameters of workplane
352   boost::shared_ptr<ModelAPI_Attribute> aDirX    = theSketch->data()->attribute(SKETCH_ATTR_DIRX);
353   boost::shared_ptr<ModelAPI_Attribute> aDirY    = theSketch->data()->attribute(SKETCH_ATTR_DIRY);
354   boost::shared_ptr<ModelAPI_Attribute> anOrigin = theSketch->data()->attribute(SKETCH_ATTR_ORIGIN);
355   // Transform them into SolveSpace format
356   Slvs_hEntity aNormalWP = addNormal(aDirX, aDirY);
357   if (!aNormalWP) return false;
358   Slvs_hEntity anOriginWP = addEntity(anOrigin);
359   if (!anOriginWP) return false;
360   // Create workplane
361   myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
362   mySketch = theSketch;
363   // Workplane should be added to the list of entities
364   myEntities.push_back(myWorkplane);
365   return true;
366 }
367
368 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addParameter(double theParam)
369 {
370   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
371   myParams.push_back(aParam);
372   return aParam.h;
373 }
374
375 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
376                 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
377 {
378   // Constraint for coincidence of two points
379   boost::shared_ptr<SketchPlugin_ConstraintCoincidence> aPtEquiv = 
380     boost::dynamic_pointer_cast<SketchPlugin_ConstraintCoincidence>(theConstraint);
381   if (aPtEquiv)
382   {
383     // Verify the constraint has only two attributes and they are points
384     int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point 
385     int aPt3d = 0; // bit-mapped field, the same information for 3D points
386     for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
387     {
388       boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr = 
389         boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
390           theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
391         );
392       // Verify the attribute is a 2D point
393       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D = 
394         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
395       if (aPoint2D)
396       {
397         aPt2d |= (1 << indAttr);
398         continue;
399       }
400       // Verify the attribute is a 3D point
401       boost::shared_ptr<GeomDataAPI_Point> aPoint3D = 
402         boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
403       if (aPoint3D)
404       {
405         aPt3d |= (1 << indAttr);
406         continue;
407       }
408       // Attribute neither 2D nor 3D point is not supported by this type of constraint
409       return SLVS_C_UNKNOWN;
410     }
411     // The constrained points should be in first and second positions,
412     // so the expected value of aPt2d or aPt3d is 3
413     if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
414       return SLVS_C_POINTS_COINCIDENT;
415     // Constraint parameters are wrong
416     return SLVS_C_UNKNOWN;
417   }
418
419   /// \todo Implement other kind of constrtaints
420
421   return SLVS_C_UNKNOWN;
422 }