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