]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_Group.cpp
Salome HOME
26b960e2ab9f86102835c19d867f5366d8ad129e
[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 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
119 {
120   if (myDOF == 0) {
121     // avoid moving elements of fully constrained sketch
122     myStorage->refresh();
123     return true;
124   }
125
126   // Create temporary Fixed constraint
127   SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
128   if (!aConstraint)
129     return false;
130   aConstraint->process(myStorage, myIsEventsBlocked);
131   if (aConstraint->error().empty())
132     setTemporary(aConstraint);
133   else
134     myStorage->notify(theFeature);
135
136   return true;
137 }
138
139 // ============================================================================
140 //  Function: resolveConstraints
141 //  Class:    SketchSolver_Group
142 //  Purpose:  solve the set of constraints for the current group
143 // ============================================================================
144 bool SketchSolver_Group::resolveConstraints()
145 {
146   // check the "Multi" constraints do not drop sketch into infinite loop
147   if (myMultiConstraintUpdateStack > 1) {
148     myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
149     // generate error message due to loop update of the sketch
150     getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
151       ->setValue(SketchSolver_Error::INFINITE_LOOP());
152     sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
153     return false;
154   }
155
156   bool aResolved = false;
157   bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
158   if (myStorage->isNeedToResolve() &&
159       (!isGroupEmpty || !myConflictingConstraints.empty() ||
160         myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
161
162     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
163     try {
164       if (!isGroupEmpty && myMultiConstraintUpdateStack <= 1)
165         aResult = mySketchSolver->solve();
166     } catch (...) {
167       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
168         ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
169       if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
170           myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
171         // the error message should be changed before sending the message
172         sendMessage(EVENT_SOLVER_FAILED);
173         myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
174       }
175       mySketchSolver->undo();
176       return false;
177     }
178     // solution succeeded, store results into correspondent attributes
179     if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
180         aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
181       myStorage->setNeedToResolve(false);
182       myStorage->refresh();
183
184       // additional check that copied entities used in Mirror and other "Multi" constraints
185       // is not connected with their originals by constraints.
186       myMultiConstraintUpdateStack += 1;
187       updateMultiConstraints();
188       aResolved = true;
189       if (myStorage->isNeedToResolve())
190         aResolved = resolveConstraints();
191
192       if (aResolved) {
193         myMultiConstraintUpdateStack -= 1;
194
195         if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
196             myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
197           getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
198           std::set<ObjectPtr> aConflicting = myConflictingConstraints;
199           myConflictingConstraints.clear();
200           myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
201           // the error message should be changed before sending the message
202           sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
203         }
204       }
205
206       // show degrees of freedom
207       computeDoF();
208     } else {
209       mySketchSolver->undo();
210       if (!myConstraints.empty()) {
211         // the error message should be changed before sending the message
212         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
213           ->setValue(SketchSolver_Error::CONSTRAINTS());
214         if (myPrevResult != aResult ||
215             myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
216             myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
217           // Obtain list of conflicting constraints
218           std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
219
220           if (!myConflictingConstraints.empty()) {
221             std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
222             for (; anIt != aConflicting.end(); ++anIt)
223               myConflictingConstraints.erase(*anIt);
224             if (!myConflictingConstraints.empty()) {
225               // some constraints does not conflict, send corresponding message
226               sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
227             }
228           }
229           myConflictingConstraints = aConflicting;
230           if (!myConflictingConstraints.empty())
231             sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
232           myPrevResult = aResult;
233         }
234       }
235     }
236
237   } else if (isGroupEmpty && isWorkplaneValid())
238     computeDoF();
239   removeTemporaryConstraints();
240   myStorage->setNeedToResolve(false);
241   return aResolved;
242 }
243
244 // ============================================================================
245 //  Function: computeDoF
246 //  Class:    SketchSolver_Group
247 //  Purpose:  compute DoF of the sketch and set corresponding field
248 // ============================================================================
249 void SketchSolver_Group::computeDoF()
250 {
251   std::ostringstream aDoFMsg;
252   int aDoF = mySketchSolver->dof();
253   if (aDoF == 0)
254     aDoFMsg << "Sketch is fully fixed (DoF = 0)";
255   else
256     aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
257   mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
258
259   if (aDoF > 0 && myDOF == 0)
260     sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
261   else if (aDoF == 0 && myDOF > 0)
262     sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
263   else if (aDoF < 0)
264     sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
265
266   myDOF = aDoF;
267 }
268
269 // ============================================================================
270 //  Function: repairConsistency
271 //  Class:    SketchSolver_Group
272 //  Purpose:  search removed entities and constraints
273 // ============================================================================
274 void SketchSolver_Group::repairConsistency()
275 {
276   if (!myStorage->isConsistent()) {
277     // remove invalid constraints
278     std::set<ConstraintPtr> anInvalidConstraints;
279     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
280     for (; aCIter != myConstraints.end(); ++aCIter) {
281       if (!aCIter->first->data() || !aCIter->first->data()->isValid())
282         anInvalidConstraints.insert(aCIter->first);
283     }
284     std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
285     for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
286       removeConstraint(*aRemoveIt);
287
288     // remove invalid features
289     myStorage->removeInvalidEntities();
290
291     // show DoF
292     computeDoF();
293   }
294 }
295
296 // ============================================================================
297 //  Function: removeTemporaryConstraints
298 //  Class:    SketchSolver_Group
299 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
300 //            resolving the set of constraints
301 // ============================================================================
302 void SketchSolver_Group::removeTemporaryConstraints()
303 {
304   if (!myTempConstraints.empty()) {
305     mySketchSolver->removeConstraint(CID_MOVEMENT);
306
307     std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
308     for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
309       (*aTmpIt)->remove();
310
311     myTempConstraints.clear();
312   }
313
314   myStorage->setNeedToResolve(false);
315 }
316
317 // ============================================================================
318 //  Function: removeConstraint
319 //  Class:    SketchSolver_Group
320 //  Purpose:  remove constraint and all unused entities
321 // ============================================================================
322 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
323 {
324   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
325   for (; aCIter != myConstraints.end(); aCIter++)
326     if (aCIter->first == theConstraint) {
327       aCIter->second->remove(); // the constraint is not fully removed
328
329       // constraint is removed => reset stack of "multi" constraints updates
330       myMultiConstraintUpdateStack = 0;
331       break;
332     }
333   if (aCIter != myConstraints.end())
334     myConstraints.erase(aCIter);
335 }
336
337 // ============================================================================
338 //  Function: setTemporary
339 //  Class:    SketchSolver_Group
340 //  Purpose:  append given constraint to the group of temporary constraints
341 // ============================================================================
342 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
343 {
344   myTempConstraints.insert(theConstraint);
345 }
346
347 // ============================================================================
348 //  Function: blockEvents
349 //  Class:    SketchSolver_Group
350 //  Purpose:  block or unblock events from features in this group
351 // ============================================================================
352 void SketchSolver_Group::blockEvents(bool isBlocked)
353 {
354   if (myIsEventsBlocked == isBlocked)
355     return;
356
357   // block/unblock events from the features in the storage
358   myStorage->blockEvents(isBlocked);
359
360   // block/unblock events from constraints
361   ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
362   for (; aCIt != myConstraints.end(); ++aCIt)
363     aCIt->second->blockEvents(isBlocked);
364
365   myIsEventsBlocked = isBlocked;
366 }
367
368 // ============================================================================
369 //  Function: updateMultiConstraints
370 //  Class:    SketchSolver_Group
371 //  Purpose:  update multi constraints
372 // ============================================================================
373 void SketchSolver_Group::updateMultiConstraints()
374 {
375   ConstraintConstraintMap::iterator anIt = myConstraints.begin();
376   for (; anIt != myConstraints.end(); ++anIt) {
377     if (anIt->first->getKind() == SketchPlugin_ConstraintMirror::ID() ||
378         anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
379         anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
380       anIt->second->update();
381   }
382 }