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 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
121 // avoid moving elements of fully constrained sketch
122 myStorage->refresh();
126 // Create temporary Fixed constraint
127 SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
130 aConstraint->process(myStorage, myIsEventsBlocked);
131 if (aConstraint->error().empty())
132 setTemporary(aConstraint);
134 myStorage->notify(theFeature);
139 // ============================================================================
140 // Function: resolveConstraints
141 // Class: SketchSolver_Group
142 // Purpose: solve the set of constraints for the current group
143 // ============================================================================
144 bool SketchSolver_Group::resolveConstraints()
146 // check the "Multi" constraints do not drop sketch into infinite loop
147 if (myMultiConstraintUpdateStack > 1) {
148 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
149 // generate error message due to loop update of the sketch
150 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
151 ->setValue(SketchSolver_Error::INFINITE_LOOP());
152 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
156 bool aResolved = false;
157 bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
158 if (myStorage->isNeedToResolve() &&
159 (!isGroupEmpty || !myConflictingConstraints.empty() ||
160 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
162 PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
164 if (!isGroupEmpty && myMultiConstraintUpdateStack <= 1)
165 aResult = mySketchSolver->solve();
167 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
168 ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
169 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
170 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
171 // the error message should be changed before sending the message
172 sendMessage(EVENT_SOLVER_FAILED);
173 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
175 mySketchSolver->undo();
178 // solution succeeded, store results into correspondent attributes
179 if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
180 aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
181 myStorage->setNeedToResolve(false);
182 myStorage->refresh();
184 // additional check that copied entities used in Mirror and other "Multi" constraints
185 // is not connected with their originals by constraints.
186 myMultiConstraintUpdateStack += 1;
187 updateMultiConstraints();
189 if (myStorage->isNeedToResolve())
190 aResolved = resolveConstraints();
193 myMultiConstraintUpdateStack -= 1;
195 if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
196 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
197 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
198 std::set<ObjectPtr> aConflicting = myConflictingConstraints;
199 myConflictingConstraints.clear();
200 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
201 // the error message should be changed before sending the message
202 sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
206 // show degrees of freedom
209 mySketchSolver->undo();
210 if (!myConstraints.empty()) {
211 // the error message should be changed before sending the message
212 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
213 ->setValue(SketchSolver_Error::CONSTRAINTS());
214 if (myPrevResult != aResult ||
215 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
216 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
217 // Obtain list of conflicting constraints
218 std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
220 if (!myConflictingConstraints.empty()) {
221 std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
222 for (; anIt != aConflicting.end(); ++anIt)
223 myConflictingConstraints.erase(*anIt);
224 if (!myConflictingConstraints.empty()) {
225 // some constraints does not conflict, send corresponding message
226 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
229 myConflictingConstraints = aConflicting;
230 if (!myConflictingConstraints.empty())
231 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
232 myPrevResult = aResult;
237 } else if (isGroupEmpty && isWorkplaneValid())
239 removeTemporaryConstraints();
240 myStorage->setNeedToResolve(false);
244 // ============================================================================
245 // Function: computeDoF
246 // Class: SketchSolver_Group
247 // Purpose: compute DoF of the sketch and set corresponding field
248 // ============================================================================
249 void SketchSolver_Group::computeDoF()
251 std::ostringstream aDoFMsg;
252 int aDoF = mySketchSolver->dof();
254 aDoFMsg << "Sketch is fully fixed (DoF = 0)";
256 aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
257 mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
259 if (aDoF > 0 && myDOF == 0)
260 sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
261 else if (aDoF == 0 && myDOF > 0)
262 sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
264 sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
269 // ============================================================================
270 // Function: repairConsistency
271 // Class: SketchSolver_Group
272 // Purpose: search removed entities and constraints
273 // ============================================================================
274 void SketchSolver_Group::repairConsistency()
276 if (!myStorage->isConsistent()) {
277 // remove invalid constraints
278 std::set<ConstraintPtr> anInvalidConstraints;
279 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
280 for (; aCIter != myConstraints.end(); ++aCIter) {
281 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
282 anInvalidConstraints.insert(aCIter->first);
284 std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
285 for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
286 removeConstraint(*aRemoveIt);
288 // remove invalid features
289 myStorage->removeInvalidEntities();
296 // ============================================================================
297 // Function: removeTemporaryConstraints
298 // Class: SketchSolver_Group
299 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
300 // resolving the set of constraints
301 // ============================================================================
302 void SketchSolver_Group::removeTemporaryConstraints()
304 if (!myTempConstraints.empty()) {
305 mySketchSolver->removeConstraint(CID_MOVEMENT);
307 std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
308 for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
311 myTempConstraints.clear();
314 myStorage->setNeedToResolve(false);
317 // ============================================================================
318 // Function: removeConstraint
319 // Class: SketchSolver_Group
320 // Purpose: remove constraint and all unused entities
321 // ============================================================================
322 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
324 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
325 for (; aCIter != myConstraints.end(); aCIter++)
326 if (aCIter->first == theConstraint) {
327 aCIter->second->remove(); // the constraint is not fully removed
329 // constraint is removed => reset stack of "multi" constraints updates
330 myMultiConstraintUpdateStack = 0;
333 if (aCIter != myConstraints.end())
334 myConstraints.erase(aCIter);
337 // ============================================================================
338 // Function: setTemporary
339 // Class: SketchSolver_Group
340 // Purpose: append given constraint to the group of temporary constraints
341 // ============================================================================
342 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
344 myTempConstraints.insert(theConstraint);
347 // ============================================================================
348 // Function: blockEvents
349 // Class: SketchSolver_Group
350 // Purpose: block or unblock events from features in this group
351 // ============================================================================
352 void SketchSolver_Group::blockEvents(bool isBlocked)
354 if (myIsEventsBlocked == isBlocked)
357 // block/unblock events from the features in the storage
358 myStorage->blockEvents(isBlocked);
360 // block/unblock events from constraints
361 ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
362 for (; aCIt != myConstraints.end(); ++aCIt)
363 aCIt->second->blockEvents(isBlocked);
365 myIsEventsBlocked = isBlocked;
368 // ============================================================================
369 // Function: updateMultiConstraints
370 // Class: SketchSolver_Group
371 // Purpose: update multi constraints
372 // ============================================================================
373 void SketchSolver_Group::updateMultiConstraints()
375 ConstraintConstraintMap::iterator anIt = myConstraints.begin();
376 for (; anIt != myConstraints.end(); ++anIt) {
377 if (anIt->first->getKind() == SketchPlugin_ConstraintMirror::ID() ||
378 anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
379 anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
380 anIt->second->update();