1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_Group.cpp
4 // Created: 27 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_Group.h"
8 #include <SketchSolver_Error.h>
9 #include <SketchSolver_Manager.h>
11 #include <PlaneGCSSolver_Solver.h>
12 #include <PlaneGCSSolver_Storage.h>
13 #include <PlaneGCSSolver_Tools.h>
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>
24 static void sendMessage(const char* theMessageName)
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);
31 static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
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);
40 static void sendMessage(const char* theMessageName,
41 const CompositeFeaturePtr& theSketch,
44 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
45 std::shared_ptr<ModelAPI_SolverFailedMessage>(
46 new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
48 std::set<ObjectPtr> anObjects;
49 anObjects.insert(theSketch);
50 aMessage->setObjects(anObjects);
51 aMessage->dof(theDOF);
53 Events_Loop::loop()->send(aMessage);
58 // ========================================================
59 // ========= SketchSolver_Group ===============
60 // ========================================================
62 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
63 : mySketch(theWorkplane),
64 myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
66 myIsEventsBlocked(false),
67 myMultiConstraintUpdateStack(0)
69 mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
70 myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
73 SketchSolver_Group::~SketchSolver_Group()
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();
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)
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);
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();
103 myConstraints[theConstraint] = aConstraint;
106 myConstraints[theConstraint]->update();
108 // constraint is created/updated => reset stack of "multi" constraints updates
109 myMultiConstraintUpdateStack = 0;
113 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
115 return myStorage->update(theFeature);
118 #ifdef SUPPORT_NEW_MOVE
119 template <class Type>
120 static SolverConstraintPtr move(StoragePtr theStorage,
121 SolverPtr theSketchSolver,
123 bool theEventsBlocked,
124 Type theFeatureOrPoint,
125 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
126 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
128 bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
129 if (theSketchDOF == 0 && isEntityExists) {
130 // avoid moving elements of fully constrained sketch
131 theStorage->refresh();
132 return SolverConstraintPtr();
135 // Create temporary Fixed constraint
136 std::shared_ptr<SketchSolver_ConstraintMovement> aConstraint =
137 PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint);
139 SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
140 if (aConstraint->error().empty()) {
141 if (!theStorage->isEmpty())
142 theStorage->setNeedToResolve(true);
144 theSketchSolver->initialize();
145 aConstraint->startPoint(theFrom);
146 aConstraint->moveTo(theTo);
148 theStorage->notify(aConstraint->movedFeature());
154 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature,
155 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
156 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
158 SolverConstraintPtr aConstraint =
159 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
160 setTemporary(aConstraint);
164 bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
165 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
166 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
168 SolverConstraintPtr aConstraint =
169 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
170 setTemporary(aConstraint);
174 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
176 bool isFeatureExists = (myStorage->entity(theFeature).get() != 0);
177 if (myDOF == 0 && isFeatureExists) {
178 // avoid moving elements of fully constrained sketch
179 myStorage->refresh();
183 // Create temporary Fixed constraint
184 std::shared_ptr<SketchSolver_ConstraintFixed> aConstraint =
185 PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
188 SolverConstraintPtr(aConstraint)->process(myStorage, myIsEventsBlocked);
189 if (aConstraint->error().empty()) {
190 setTemporary(aConstraint);
191 if (!myStorage->isEmpty())
192 myStorage->setNeedToResolve(true);
194 mySketchSolver->initialize();
195 aConstraint->moveFeature();
197 myStorage->notify(theFeature);
203 // ============================================================================
204 // Function: resolveConstraints
205 // Class: SketchSolver_Group
206 // Purpose: solve the set of constraints for the current group
207 // ============================================================================
208 bool SketchSolver_Group::resolveConstraints()
210 static const int MAX_STACK_SIZE = 5;
211 // check the "Multi" constraints do not drop sketch into infinite loop
212 if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) {
213 myMultiConstraintUpdateStack = 0;
214 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
215 // generate error message due to loop update of the sketch
216 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
217 ->setValue(SketchSolver_Error::INFINITE_LOOP());
218 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
222 bool aResolved = false;
223 bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
224 if (myStorage->isNeedToResolve() &&
225 (!isGroupEmpty || !myConflictingConstraints.empty() ||
226 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
228 PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
231 aResult = mySketchSolver->solve();
232 if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
233 !myTempConstraints.empty()) {
234 mySketchSolver->undo();
235 removeTemporaryConstraints();
236 aResult = mySketchSolver->solve();
239 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
240 ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
241 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
242 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
243 // the error message should be changed before sending the message
244 sendMessage(EVENT_SOLVER_FAILED);
245 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
247 mySketchSolver->undo();
250 // solution succeeded, store results into correspondent attributes
251 if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
252 aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
253 myStorage->setNeedToResolve(false);
254 myStorage->refresh();
256 // additional check that copied entities used in Mirror and other "Multi" constraints
257 // is not connected with their originals by constraints.
258 myMultiConstraintUpdateStack += 1;
260 if (myStorage->isNeedToResolve())
261 aResolved = resolveConstraints();
264 myMultiConstraintUpdateStack -= 1;
266 if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
267 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
268 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
269 std::set<ObjectPtr> aConflicting = myConflictingConstraints;
270 myConflictingConstraints.clear();
271 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
272 // the error message should be changed before sending the message
273 sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
277 // show degrees of freedom
280 mySketchSolver->undo();
281 if (!myConstraints.empty()) {
282 // the error message should be changed before sending the message
283 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
284 ->setValue(SketchSolver_Error::CONSTRAINTS());
285 if (myPrevResult != aResult ||
286 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
287 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
288 // Obtain list of conflicting constraints
289 std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
291 if (!myConflictingConstraints.empty()) {
292 std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
293 for (; anIt != aConflicting.end(); ++anIt)
294 myConflictingConstraints.erase(*anIt);
295 if (!myConflictingConstraints.empty()) {
296 // some constraints does not conflict, send corresponding message
297 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
300 myConflictingConstraints = aConflicting;
301 if (!myConflictingConstraints.empty())
302 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
303 myPrevResult = aResult;
308 } else if (isGroupEmpty && isWorkplaneValid())
310 removeTemporaryConstraints();
311 myStorage->setNeedToResolve(false);
315 // ============================================================================
316 // Function: computeDoF
317 // Class: SketchSolver_Group
318 // Purpose: compute DoF of the sketch and set corresponding field
319 // ============================================================================
320 void SketchSolver_Group::computeDoF()
322 std::ostringstream aDoFMsg;
323 int aDoF = mySketchSolver->dof();
324 /// "DoF = 0" content of string value is used in PartSet by Sketch edit
325 /// If it is changed, it should be corrected also there
327 aDoFMsg << "Sketch is fully fixed (DoF = 0)";
329 aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
330 mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
332 if (aDoF > 0 && myDOF == 0)
333 sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
334 else if (aDoF == 0 && myDOF > 0)
335 sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
337 sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
342 // ============================================================================
343 // Function: repairConsistency
344 // Class: SketchSolver_Group
345 // Purpose: search removed entities and constraints
346 // ============================================================================
347 void SketchSolver_Group::repairConsistency()
349 if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
350 // remove invalid constraints
351 std::set<ConstraintPtr> anInvalidConstraints;
352 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
353 for (; aCIter != myConstraints.end(); ++aCIter) {
354 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
355 anInvalidConstraints.insert(aCIter->first);
357 std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
358 for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
359 removeConstraint(*aRemoveIt);
361 // remove invalid features
362 myStorage->removeInvalidEntities();
369 // ============================================================================
370 // Function: removeTemporaryConstraints
371 // Class: SketchSolver_Group
372 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
373 // resolving the set of constraints
374 // ============================================================================
375 void SketchSolver_Group::removeTemporaryConstraints()
377 if (!myTempConstraints.empty()) {
378 mySketchSolver->removeConstraint(CID_MOVEMENT);
380 std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
381 for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
384 myTempConstraints.clear();
387 myStorage->setNeedToResolve(false);
390 // ============================================================================
391 // Function: removeConstraint
392 // Class: SketchSolver_Group
393 // Purpose: remove constraint and all unused entities
394 // ============================================================================
395 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
397 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
398 for (; aCIter != myConstraints.end(); aCIter++)
399 if (aCIter->first == theConstraint) {
400 aCIter->second->remove(); // the constraint is not fully removed
402 // constraint is removed => reset stack of "multi" constraints updates
403 myMultiConstraintUpdateStack = 0;
406 if (aCIter != myConstraints.end())
407 myConstraints.erase(aCIter);
410 // ============================================================================
411 // Function: setTemporary
412 // Class: SketchSolver_Group
413 // Purpose: append given constraint to the group of temporary constraints
414 // ============================================================================
415 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
418 myTempConstraints.insert(theConstraint);
421 // ============================================================================
422 // Function: blockEvents
423 // Class: SketchSolver_Group
424 // Purpose: block or unblock events from features in this group
425 // ============================================================================
426 void SketchSolver_Group::blockEvents(bool isBlocked)
428 if (myIsEventsBlocked == isBlocked)
431 // block/unblock events from the features in the storage
432 myStorage->blockEvents(isBlocked);
434 // block/unblock events from constraints
435 ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
436 for (; aCIt != myConstraints.end(); ++aCIt)
437 aCIt->second->blockEvents(isBlocked);
439 myIsEventsBlocked = isBlocked;
442 bool SketchSolver_Group::areConstraintsValid() const
444 // Check the constraints are valid
445 ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin();
446 for (; aCIter != myConstraints.end(); ++aCIter)
447 if (!aCIter->first->data() || !aCIter->first->data()->isValid())