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