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