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