]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_Group.cpp
Salome HOME
840c7b736ca94ed52c3b6d968d8e1f2d790043bc
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_Group.cpp
4 // Created: 27 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_Group.h"
8 #include <SketchSolver_Error.h>
9 #include <SketchSolver_Manager.h>
10
11 #include <PlaneGCSSolver_Solver.h>
12 #include <PlaneGCSSolver_Storage.h>
13 #include <PlaneGCSSolver_Tools.h>
14
15 #include <Events_InfoMessage.h>
16 #include <ModelAPI_AttributeString.h>
17 #include <ModelAPI_Events.h>
18 #include <SketchPlugin_ConstraintMirror.h>
19 #include <SketchPlugin_ConstraintRigid.h>
20 #include <SketchPlugin_MultiRotation.h>
21 #include <SketchPlugin_MultiTranslation.h>
22
23
24 static void sendMessage(const char* theMessageName)
25 {
26   std::shared_ptr<Events_Message> aMessage = std::shared_ptr<Events_Message>(
27       new Events_Message(Events_Loop::eventByName(theMessageName)));
28   Events_Loop::loop()->send(aMessage);
29 }
30
31 static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
32 {
33   std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
34       std::shared_ptr<ModelAPI_SolverFailedMessage>(
35       new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
36   aMessage->setObjects(theConflicting);
37   Events_Loop::loop()->send(aMessage);
38 }
39
40 static void sendMessage(const char* theMessageName,
41                         const CompositeFeaturePtr& theSketch,
42                         const int theDOF)
43 {
44   std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
45       std::shared_ptr<ModelAPI_SolverFailedMessage>(
46       new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
47
48   std::set<ObjectPtr> anObjects;
49   anObjects.insert(theSketch);
50   aMessage->setObjects(anObjects);
51   aMessage->dof(theDOF);
52
53   Events_Loop::loop()->send(aMessage);
54 }
55
56
57
58 // ========================================================
59 // =========       SketchSolver_Group       ===============
60 // ========================================================
61
62 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
63   : mySketch(theWorkplane),
64     myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
65     myDOF(0),
66     myIsEventsBlocked(false),
67     myMultiConstraintUpdateStack(0)
68 {
69   mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
70   myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
71 }
72
73 SketchSolver_Group::~SketchSolver_Group()
74 {
75   myConstraints.clear();
76   // send the message that there is no more conflicting constraints
77   if (!myConflictingConstraints.empty()) {
78     sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
79     myConflictingConstraints.clear();
80   }
81 }
82
83 // ============================================================================
84 //  Function: changeConstraint
85 //  Class:    SketchSolver_Group
86 //  Purpose:  create/update the constraint in the group
87 // ============================================================================
88 bool SketchSolver_Group::changeConstraint(
89     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
90 {
91   bool isNewConstraint = myConstraints.find(theConstraint) == myConstraints.end();
92   if (isNewConstraint) {
93     // Add constraint to the current group
94     SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createConstraint(theConstraint);
95     if (!aConstraint)
96       return false;
97     aConstraint->process(myStorage, myIsEventsBlocked);
98     if (!aConstraint->error().empty()) {
99       if (aConstraint->error() == SketchSolver_Error::NOT_INITIALIZED())
100         return false; // some attribute are not initialized yet, don't show message
101       Events_InfoMessage("SketchSolver_Group", aConstraint->error(), this).send();
102     }
103     myConstraints[theConstraint] = aConstraint;
104   }
105   else
106     myConstraints[theConstraint]->update();
107
108   // constraint is created/updated => reset stack of "multi" constraints updates
109   myMultiConstraintUpdateStack = 0;
110   return true;
111 }
112
113 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
114 {
115   return myStorage->update(theFeature);
116 }
117
118 template <class Type>
119 static SolverConstraintPtr move(StoragePtr theStorage,
120                                 SolverPtr theSketchSolver,
121                                 int theSketchDOF,
122                                 bool theEventsBlocked,
123                                 Type theFeatureOrPoint,
124                                 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
125                                 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
126 {
127   bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
128   if (theSketchDOF == 0 && isEntityExists) {
129     // avoid moving elements of fully constrained sketch
130     theStorage->refresh();
131     return SolverConstraintPtr();
132   }
133
134   // Create temporary Fixed constraint
135   std::shared_ptr<SketchSolver_ConstraintMovement> aConstraint =
136       PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint);
137   if (aConstraint) {
138     SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
139     if (aConstraint->error().empty()) {
140       if (!theStorage->isEmpty())
141         theStorage->setNeedToResolve(true);
142
143       theSketchSolver->initialize();
144       aConstraint->startPoint(theFrom);
145       aConstraint->moveTo(theTo);
146     } else
147       theStorage->notify(aConstraint->movedFeature());
148   }
149
150   return aConstraint;
151 }
152
153 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature,
154                                      const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
155                                      const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
156 {
157   SolverConstraintPtr aConstraint =
158       move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
159   setTemporary(aConstraint);
160   return true;
161 }
162
163 bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
164                                    const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
165                                    const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
166 {
167   SolverConstraintPtr aConstraint =
168       move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
169   setTemporary(aConstraint);
170   return true;
171 }
172
173 // ============================================================================
174 //  Function: resolveConstraints
175 //  Class:    SketchSolver_Group
176 //  Purpose:  solve the set of constraints for the current group
177 // ============================================================================
178 bool SketchSolver_Group::resolveConstraints()
179 {
180   static const int MAX_STACK_SIZE = 5;
181   // check the "Multi" constraints do not drop sketch into infinite loop
182   if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) {
183     myMultiConstraintUpdateStack = 0;
184     myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
185     // generate error message due to loop update of the sketch
186     getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
187       ->setValue(SketchSolver_Error::INFINITE_LOOP());
188     sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
189     return false;
190   }
191
192   bool aResolved = false;
193   bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
194   if (myStorage->isNeedToResolve() &&
195       (!isGroupEmpty || !myConflictingConstraints.empty() ||
196         myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
197
198     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
199     try {
200       if (!isGroupEmpty)
201         aResult = mySketchSolver->solve();
202       if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
203           !myTempConstraints.empty()) {
204         mySketchSolver->undo();
205         removeTemporaryConstraints();
206         aResult = mySketchSolver->solve();
207       }
208     } catch (...) {
209       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
210         ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
211       if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
212           myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
213         // the error message should be changed before sending the message
214         sendMessage(EVENT_SOLVER_FAILED);
215         myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
216       }
217       mySketchSolver->undo();
218       return false;
219     }
220     // solution succeeded, store results into correspondent attributes
221     if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
222         aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
223       myStorage->setNeedToResolve(false);
224       myStorage->refresh();
225
226       // additional check that copied entities used in Mirror and other "Multi" constraints
227       // is not connected with their originals by constraints.
228       myMultiConstraintUpdateStack += 1;
229       aResolved = true;
230       if (myStorage->isNeedToResolve())
231         aResolved = resolveConstraints();
232
233       if (aResolved) {
234         myMultiConstraintUpdateStack -= 1;
235
236         if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
237             myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
238           getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
239           std::set<ObjectPtr> aConflicting = myConflictingConstraints;
240           myConflictingConstraints.clear();
241           myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
242           // the error message should be changed before sending the message
243           sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
244         }
245       }
246
247       // show degrees of freedom
248       computeDoF();
249     } else {
250       mySketchSolver->undo();
251       if (!myConstraints.empty()) {
252         // the error message should be changed before sending the message
253         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
254           ->setValue(SketchSolver_Error::CONSTRAINTS());
255         if (myPrevResult != aResult ||
256             myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
257             myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
258           // Obtain list of conflicting constraints
259           std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
260
261           if (!myConflictingConstraints.empty()) {
262             std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
263             for (; anIt != aConflicting.end(); ++anIt)
264               myConflictingConstraints.erase(*anIt);
265             if (!myConflictingConstraints.empty()) {
266               // some constraints does not conflict, send corresponding message
267               sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
268             }
269           }
270           myConflictingConstraints = aConflicting;
271           if (!myConflictingConstraints.empty())
272             sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
273           myPrevResult = aResult;
274         }
275       }
276     }
277
278   } else if (isGroupEmpty && isWorkplaneValid())
279     computeDoF();
280   removeTemporaryConstraints();
281   myStorage->setNeedToResolve(false);
282   return aResolved;
283 }
284
285 // ============================================================================
286 //  Function: computeDoF
287 //  Class:    SketchSolver_Group
288 //  Purpose:  compute DoF of the sketch and set corresponding field
289 // ============================================================================
290 void SketchSolver_Group::computeDoF()
291 {
292   std::ostringstream aDoFMsg;
293   int aDoF = mySketchSolver->dof();
294   /// "DoF = 0" content of string value is used in PartSet by Sketch edit
295   /// If it is changed, it should be corrected also there
296   if (aDoF == 0)
297     aDoFMsg << "Sketch is fully fixed (DoF = 0)";
298   else
299     aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
300   mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
301
302   if (aDoF > 0 && myDOF == 0)
303     sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
304   else if (aDoF == 0 && myDOF > 0)
305     sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
306   else if (aDoF < 0)
307     sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
308
309   myDOF = aDoF;
310 }
311
312 // ============================================================================
313 //  Function: repairConsistency
314 //  Class:    SketchSolver_Group
315 //  Purpose:  search removed entities and constraints
316 // ============================================================================
317 void SketchSolver_Group::repairConsistency()
318 {
319   if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
320     // remove invalid constraints
321     std::set<ConstraintPtr> anInvalidConstraints;
322     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
323     for (; aCIter != myConstraints.end(); ++aCIter) {
324       if (!aCIter->first->data() || !aCIter->first->data()->isValid())
325         anInvalidConstraints.insert(aCIter->first);
326     }
327     std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
328     for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
329       removeConstraint(*aRemoveIt);
330
331     // remove invalid features
332     myStorage->removeInvalidEntities();
333
334     // show DoF
335     computeDoF();
336   }
337 }
338
339 // ============================================================================
340 //  Function: removeTemporaryConstraints
341 //  Class:    SketchSolver_Group
342 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
343 //            resolving the set of constraints
344 // ============================================================================
345 void SketchSolver_Group::removeTemporaryConstraints()
346 {
347   if (!myTempConstraints.empty()) {
348     mySketchSolver->removeConstraint(CID_MOVEMENT);
349
350     std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
351     for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
352       (*aTmpIt)->remove();
353
354     myTempConstraints.clear();
355   }
356
357   myStorage->setNeedToResolve(false);
358 }
359
360 // ============================================================================
361 //  Function: removeConstraint
362 //  Class:    SketchSolver_Group
363 //  Purpose:  remove constraint and all unused entities
364 // ============================================================================
365 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
366 {
367   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
368   for (; aCIter != myConstraints.end(); aCIter++)
369     if (aCIter->first == theConstraint) {
370       aCIter->second->remove(); // the constraint is not fully removed
371
372       // constraint is removed => reset stack of "multi" constraints updates
373       myMultiConstraintUpdateStack = 0;
374       break;
375     }
376   if (aCIter != myConstraints.end())
377     myConstraints.erase(aCIter);
378 }
379
380 // ============================================================================
381 //  Function: setTemporary
382 //  Class:    SketchSolver_Group
383 //  Purpose:  append given constraint to the group of temporary constraints
384 // ============================================================================
385 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
386 {
387   if (theConstraint)
388     myTempConstraints.insert(theConstraint);
389 }
390
391 // ============================================================================
392 //  Function: blockEvents
393 //  Class:    SketchSolver_Group
394 //  Purpose:  block or unblock events from features in this group
395 // ============================================================================
396 void SketchSolver_Group::blockEvents(bool isBlocked)
397 {
398   if (myIsEventsBlocked == isBlocked)
399     return;
400
401   // block/unblock events from the features in the storage
402   myStorage->blockEvents(isBlocked);
403
404   // block/unblock events from constraints
405   ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
406   for (; aCIt != myConstraints.end(); ++aCIt)
407     aCIt->second->blockEvents(isBlocked);
408
409   myIsEventsBlocked = isBlocked;
410 }
411
412 bool SketchSolver_Group::areConstraintsValid() const
413 {
414   // Check the constraints are valid
415   ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin();
416   for (; aCIter != myConstraints.end(); ++aCIter)
417     if (!aCIter->first->data() || !aCIter->first->data()->isValid())
418       return false;
419   return true;
420 }