1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
20 #include "SketchSolver_Group.h"
21 #include <SketchSolver_Error.h>
22 #include <SketchSolver_Manager.h>
24 #include <PlaneGCSSolver_Solver.h>
25 #include <PlaneGCSSolver_Storage.h>
26 #include <PlaneGCSSolver_Tools.h>
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>
37 static void sendMessage(const char* theMessageName)
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);
44 static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
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);
53 static void sendMessage(const char* theMessageName,
54 const CompositeFeaturePtr& theSketch,
57 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
58 std::shared_ptr<ModelAPI_SolverFailedMessage>(
59 new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
61 std::set<ObjectPtr> anObjects;
62 anObjects.insert(theSketch);
63 aMessage->setObjects(anObjects);
64 aMessage->dof(theDOF);
66 Events_Loop::loop()->send(aMessage);
71 // ========================================================
72 // ========= SketchSolver_Group ===============
73 // ========================================================
75 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
76 : mySketch(theWorkplane),
77 myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
79 myIsEventsBlocked(false),
80 myMultiConstraintUpdateStack(0)
82 mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
83 myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
86 SketchSolver_Group::~SketchSolver_Group()
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();
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)
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);
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();
116 myConstraints[theConstraint] = aConstraint;
119 myConstraints[theConstraint]->update();
121 // constraint is created/updated => reset stack of "multi" constraints updates
122 myMultiConstraintUpdateStack = 0;
126 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
128 return myStorage->update(theFeature);
131 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
133 bool isFeatureExists = (myStorage->entity(theFeature).get() != 0);
134 if (myDOF == 0 && isFeatureExists) {
135 // avoid moving elements of fully constrained sketch
136 myStorage->refresh();
140 // Create temporary Fixed constraint
141 std::shared_ptr<SketchSolver_ConstraintFixed> aConstraint =
142 PlaneGCSSolver_Tools::createMovementConstraint(theFeature);
145 SolverConstraintPtr(aConstraint)->process(myStorage, myIsEventsBlocked);
146 if (aConstraint->error().empty()) {
147 setTemporary(aConstraint);
148 if (!myStorage->isEmpty())
149 myStorage->setNeedToResolve(true);
151 mySketchSolver->initialize();
152 aConstraint->moveFeature();
155 // notify all observers that theFeature has been changed
156 myStorage->notify(theFeature);
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()
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);
179 bool aResolved = false;
180 bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
181 if (myStorage->isNeedToResolve() &&
182 (!isGroupEmpty || !myConflictingConstraints.empty() ||
183 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
185 PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
188 aResult = mySketchSolver->solve();
189 if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
190 !myTempConstraints.empty()) {
191 mySketchSolver->undo();
192 removeTemporaryConstraints();
193 aResult = mySketchSolver->solve();
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;
204 mySketchSolver->undo();
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();
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;
217 if (myStorage->isNeedToResolve())
218 aResolved = resolveConstraints();
221 myMultiConstraintUpdateStack -= 1;
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);
234 // show degrees of freedom
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);
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);
257 myConflictingConstraints = aConflicting;
258 if (!myConflictingConstraints.empty())
259 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
260 myPrevResult = aResult;
265 } else if (isGroupEmpty && isWorkplaneValid())
267 removeTemporaryConstraints();
268 myStorage->setNeedToResolve(false);
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()
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
284 aDoFMsg << "Sketch is fully fixed (DoF = 0)";
286 aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
287 mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
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);
294 sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
299 // ============================================================================
300 // Function: repairConsistency
301 // Class: SketchSolver_Group
302 // Purpose: search removed entities and constraints
303 // ============================================================================
304 void SketchSolver_Group::repairConsistency()
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);
314 std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
315 for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
316 removeConstraint(*aRemoveIt);
318 // remove invalid features
319 myStorage->removeInvalidEntities();
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()
334 if (!myTempConstraints.empty()) {
335 mySketchSolver->removeConstraint(CID_MOVEMENT);
337 std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
338 for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
341 myTempConstraints.clear();
344 myStorage->setNeedToResolve(false);
347 // ============================================================================
348 // Function: removeConstraint
349 // Class: SketchSolver_Group
350 // Purpose: remove constraint and all unused entities
351 // ============================================================================
352 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
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
359 // constraint is removed => reset stack of "multi" constraints updates
360 myMultiConstraintUpdateStack = 0;
363 if (aCIter != myConstraints.end())
364 myConstraints.erase(aCIter);
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)
374 myTempConstraints.insert(theConstraint);
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)
384 if (myIsEventsBlocked == isBlocked)
387 // block/unblock events from the features in the storage
388 myStorage->blockEvents(isBlocked);
390 // block/unblock events from constraints
391 ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
392 for (; aCIt != myConstraints.end(); ++aCIt)
393 aCIt->second->blockEvents(isBlocked);
395 myIsEventsBlocked = isBlocked;
398 bool SketchSolver_Group::areConstraintsValid() const
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())