Salome HOME
Merge branch 'master' into cgt/devCEA
[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_ConstraintRigid.h>
19
20
21 static void sendMessage(const char* theMessageName)
22 {
23   std::shared_ptr<Events_Message> aMessage = std::shared_ptr<Events_Message>(
24       new Events_Message(Events_Loop::eventByName(theMessageName)));
25   Events_Loop::loop()->send(aMessage);
26 }
27
28 static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
29 {
30   std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
31       std::shared_ptr<ModelAPI_SolverFailedMessage>(
32       new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
33   aMessage->setObjects(theConflicting);
34   Events_Loop::loop()->send(aMessage);
35 }
36
37 static void sendMessage(const char* theMessageName, const CompositeFeaturePtr& theSketch, const int theDOF)
38 {
39   std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
40       std::shared_ptr<ModelAPI_SolverFailedMessage>(
41       new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
42
43   std::set<ObjectPtr> anObjects;
44   anObjects.insert(theSketch);
45   aMessage->setObjects(anObjects);
46   aMessage->dof(theDOF);
47
48   Events_Loop::loop()->send(aMessage);
49 }
50
51
52
53 // ========================================================
54 // =========  SketchSolver_Group  ===============
55 // ========================================================
56
57 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
58   : mySketch(theWorkplane),
59     myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
60     myDOF(0),
61     myIsEventsBlocked(false)
62 {
63   mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
64   myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
65 }
66
67 SketchSolver_Group::~SketchSolver_Group()
68 {
69   myConstraints.clear();
70   // send the message that there is no more conflicting constraints
71   if (!myConflictingConstraints.empty()) {
72     sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
73     myConflictingConstraints.clear();
74   }
75 }
76
77 // ============================================================================
78 //  Function: changeConstraint
79 //  Class:    SketchSolver_Group
80 //  Purpose:  create/update the constraint in the group
81 // ============================================================================
82 bool SketchSolver_Group::changeConstraint(
83     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
84 {
85   bool isNewConstraint = myConstraints.find(theConstraint) == myConstraints.end();
86   if (isNewConstraint) {
87     // Add constraint to the current group
88     SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createConstraint(theConstraint);
89     if (!aConstraint)
90       return false;
91     aConstraint->process(myStorage, myIsEventsBlocked);
92     if (!aConstraint->error().empty()) {
93       if (aConstraint->error() == SketchSolver_Error::NOT_INITIALIZED())
94         return false; // some attribute are not initialized yet, don't show message
95       Events_InfoMessage("SketchSolver_Group", aConstraint->error(), this).send();
96     }
97     myConstraints[theConstraint] = aConstraint;
98   }
99   else
100     myConstraints[theConstraint]->update();
101   return true;
102 }
103
104 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
105 {
106   return myStorage->update(theFeature);
107 }
108
109 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
110 {
111   if (myDOF == 0) {
112     // avoid moving elements of fully constrained sketch
113     myStorage->refresh();
114     return true;
115   }
116
117   // Create temporary Fixed constraint
118   SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
119   if (!aConstraint)
120     return false;
121   aConstraint->process(myStorage, myIsEventsBlocked);
122   if (aConstraint->error().empty())
123     setTemporary(aConstraint);
124   else
125     myStorage->notify(theFeature);
126
127   return true;
128 }
129
130 // ============================================================================
131 //  Function: resolveConstraints
132 //  Class:    SketchSolver_Group
133 //  Purpose:  solve the set of constraints for the current group
134 // ============================================================================
135 bool SketchSolver_Group::resolveConstraints()
136 {
137   bool aResolved = false;
138   bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
139   if (myStorage->isNeedToResolve() &&
140       (!isGroupEmpty || !myConflictingConstraints.empty() ||
141         myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
142
143     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
144     try {
145       if (!isGroupEmpty)
146         aResult = mySketchSolver->solve();
147     } catch (...) {
148       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
149         ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
150       if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
151           myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
152         // the error message should be changed before sending the message
153         sendMessage(EVENT_SOLVER_FAILED);
154         myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
155       }
156       mySketchSolver->undo();
157       return false;
158     }
159     // solution succeeded, store results into correspondent attributes
160     if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
161         aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
162       myStorage->setNeedToResolve(false);
163       myStorage->refresh();
164 ////      updateMultiConstraints(myConstraints);
165 ////      // multi-constraints updated some parameters, need to store them
166 ////      if (myStorage->isNeedToResolve())
167 ////        resolveConstraints();
168
169       if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
170           myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
171         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
172         std::set<ObjectPtr> aConflicting = myConflictingConstraints;
173         myConflictingConstraints.clear();
174         myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
175         // the error message should be changed before sending the message
176         sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
177       }
178
179       // show degrees of freedom
180       computeDoF();
181     } else {
182       mySketchSolver->undo();
183       if (!myConstraints.empty()) {
184         // the error message should be changed before sending the message
185         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
186           ->setValue(SketchSolver_Error::CONSTRAINTS());
187         if (myPrevResult != aResult ||
188             myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
189             myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
190           // Obtain list of conflicting constraints
191           std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
192
193           if (!myConflictingConstraints.empty()) {
194             std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
195             for (; anIt != aConflicting.end(); ++anIt)
196               myConflictingConstraints.erase(*anIt);
197             if (!myConflictingConstraints.empty()) {
198               // some constraints does not conflict, send corresponding message
199               sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
200             }
201           }
202           myConflictingConstraints = aConflicting;
203           if (!myConflictingConstraints.empty())
204             sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
205           myPrevResult = aResult;
206         }
207       }
208     }
209
210     aResolved = true;
211   } else if (!isGroupEmpty) {
212     // Check if the group contains only constraints Fixed, update parameters by stored values
213     aResolved = true;
214     ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
215     for (; aCIt != myConstraints.end(); ++aCIt)
216       if (aCIt->first->getKind() != SketchPlugin_ConstraintRigid::ID()) {
217         aResolved = false;
218         break;
219       }
220     if (aCIt == myConstraints.end())
221       myStorage->refresh();
222   } else if (isGroupEmpty && isWorkplaneValid())
223     computeDoF();
224   removeTemporaryConstraints();
225   myStorage->setNeedToResolve(false);
226   return aResolved;
227 }
228
229 // ============================================================================
230 //  Function: computeDoF
231 //  Class:    SketchSolver_Group
232 //  Purpose:  compute DoF of the sketch and set corresponding field
233 // ============================================================================
234 void SketchSolver_Group::computeDoF()
235 {
236   std::ostringstream aDoFMsg;
237   int aDoF = mySketchSolver->dof();
238   if (aDoF == 0)
239     aDoFMsg << "Sketch is fully fixed (DoF = 0)";
240   else
241     aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
242   mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
243
244   if (aDoF > 0 && myDOF == 0)
245     sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
246   else if (aDoF == 0 && myDOF > 0)
247     sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
248   else if (aDoF < 0)
249     sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
250
251   myDOF = aDoF;
252 }
253
254 // ============================================================================
255 //  Function: repairConsistency
256 //  Class:    SketchSolver_Group
257 //  Purpose:  search removed entities and constraints
258 // ============================================================================
259 void SketchSolver_Group::repairConsistency()
260 {
261   if (!myStorage->isConsistent()) {
262     // remove invalid constraints
263     std::set<ConstraintPtr> anInvalidConstraints;
264     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
265     for (; aCIter != myConstraints.end(); ++aCIter) {
266       if (!aCIter->first->data() || !aCIter->first->data()->isValid())
267         anInvalidConstraints.insert(aCIter->first);
268     }
269     std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
270     for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
271       removeConstraint(*aRemoveIt);
272
273     // remove invalid features
274     myStorage->removeInvalidEntities();
275
276     // show DoF
277     computeDoF();
278   }
279 }
280
281 // ============================================================================
282 //  Function: removeTemporaryConstraints
283 //  Class:    SketchSolver_Group
284 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
285 //            resolving the set of constraints
286 // ============================================================================
287 void SketchSolver_Group::removeTemporaryConstraints()
288 {
289   if (!myTempConstraints.empty()) {
290     mySketchSolver->removeConstraint(CID_MOVEMENT);
291
292     std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
293     for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
294       (*aTmpIt)->remove();
295
296     myTempConstraints.clear();
297   }
298
299   myStorage->setNeedToResolve(false);
300 }
301
302 // ============================================================================
303 //  Function: removeConstraint
304 //  Class:    SketchSolver_Group
305 //  Purpose:  remove constraint and all unused entities
306 // ============================================================================
307 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
308 {
309   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
310   for (; aCIter != myConstraints.end(); aCIter++)
311     if (aCIter->first == theConstraint) {
312       aCIter->second->remove(); // the constraint is not fully removed
313       break;
314     }
315   if (aCIter != myConstraints.end())
316     myConstraints.erase(aCIter);
317 }
318
319 // ============================================================================
320 //  Function: setTemporary
321 //  Class:    SketchSolver_Group
322 //  Purpose:  append given constraint to the group of temporary constraints
323 // ============================================================================
324 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
325 {
326   myTempConstraints.insert(theConstraint);
327 }
328
329 // ============================================================================
330 //  Function: blockEvents
331 //  Class:    SketchSolver_Group
332 //  Purpose:  block or unblock events from features in this group
333 // ============================================================================
334 void SketchSolver_Group::blockEvents(bool isBlocked)
335 {
336   if (myIsEventsBlocked == isBlocked)
337     return;
338
339   // block/unblock events from the features in the storage
340   myStorage->blockEvents(isBlocked);
341
342   // block/unblock events from constraints
343   ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
344   for (; aCIt != myConstraints.end(); ++aCIt)
345     aCIt->second->blockEvents(isBlocked);
346
347   myIsEventsBlocked = isBlocked;
348 }