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