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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include "SketchSolver_Group.h"
22 #include <SketchSolver_Error.h>
23 #include <SketchSolver_Manager.h>
25 #include <PlaneGCSSolver_Solver.h>
26 #include <PlaneGCSSolver_Storage.h>
27 #include <PlaneGCSSolver_Tools.h>
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>
38 static void sendMessage(const char* theMessageName)
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);
45 static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& theConflicting)
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);
54 static void sendMessage(const char* theMessageName,
55 const CompositeFeaturePtr& theSketch,
58 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
59 std::shared_ptr<ModelAPI_SolverFailedMessage>(
60 new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
62 std::set<ObjectPtr> anObjects;
63 anObjects.insert(theSketch);
64 aMessage->setObjects(anObjects);
65 aMessage->dof(theDOF);
67 Events_Loop::loop()->send(aMessage);
72 // ========================================================
73 // ========= SketchSolver_Group ===============
74 // ========================================================
76 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
77 : mySketch(theWorkplane),
78 myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
80 myIsEventsBlocked(false),
81 myMultiConstraintUpdateStack(0)
83 mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
84 myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
87 SketchSolver_Group::~SketchSolver_Group()
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();
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)
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);
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();
117 myConstraints[theConstraint] = aConstraint;
120 myConstraints[theConstraint]->update();
122 // constraint is created/updated => reset stack of "multi" constraints updates
123 myMultiConstraintUpdateStack = 0;
127 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
129 return myStorage->update(theFeature);
132 template <class Type>
133 static SolverConstraintPtr move(StoragePtr theStorage,
134 SolverPtr theSketchSolver,
136 bool theEventsBlocked,
137 Type theFeatureOrPoint,
138 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
139 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
141 bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
142 if (theSketchDOF == 0 && isEntityExists) {
143 // avoid moving elements of fully constrained sketch
144 theStorage->refresh();
145 return SolverConstraintPtr();
148 // Create temporary Fixed constraint
149 std::shared_ptr<SketchSolver_ConstraintMovement> aConstraint =
150 PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint);
152 SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
153 if (aConstraint->error().empty()) {
154 aConstraint->startPoint(theFrom);
155 theSketchSolver->initialize();
156 aConstraint->moveTo(theTo);
157 theStorage->setNeedToResolve(true);
159 theStorage->notify(aConstraint->movedFeature());
165 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature,
166 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
167 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
169 SolverConstraintPtr aConstraint =
170 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
171 setTemporary(aConstraint);
175 bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
176 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
177 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
179 SolverConstraintPtr aConstraint =
180 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
181 setTemporary(aConstraint);
185 // ============================================================================
186 // Function: resolveConstraints
187 // Class: SketchSolver_Group
188 // Purpose: solve the set of constraints for the current group
189 // ============================================================================
190 bool SketchSolver_Group::resolveConstraints()
192 static const int MAX_STACK_SIZE = 5;
193 // check the "Multi" constraints do not drop sketch into infinite loop
194 if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) {
195 myMultiConstraintUpdateStack = 0;
196 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
197 // generate error message due to loop update of the sketch
198 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
199 ->setValue(SketchSolver_Error::INFINITE_LOOP());
200 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
204 bool aResolved = false;
205 bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
206 if (myStorage->isNeedToResolve() &&
207 (!isGroupEmpty || !myConflictingConstraints.empty() ||
208 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
210 PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
213 aResult = mySketchSolver->solve();
214 if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
215 !myTempConstraints.empty()) {
216 mySketchSolver->undo();
217 removeTemporaryConstraints();
218 aResult = mySketchSolver->solve();
220 // check degenerated geometry after constraints resolving
221 if (aResult == PlaneGCSSolver_Solver::STATUS_OK)
222 aResult = myStorage->checkDegeneratedGeometry();
224 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
225 ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
226 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
227 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
228 // the error message should be changed before sending the message
229 sendMessage(EVENT_SOLVER_FAILED);
230 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
232 mySketchSolver->undo();
235 // solution succeeded, store results into correspondent attributes
236 if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
237 aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
238 myStorage->setNeedToResolve(false);
239 myStorage->refresh();
241 // additional check that copied entities used in Mirror and other "Multi" constraints
242 // is not connected with their originals by constraints.
243 myMultiConstraintUpdateStack += 1;
245 if (myStorage->isNeedToResolve())
246 aResolved = resolveConstraints();
249 myMultiConstraintUpdateStack -= 1;
251 if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
252 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
253 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
254 std::set<ObjectPtr> aConflicting = myConflictingConstraints;
255 myConflictingConstraints.clear();
256 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
257 // the error message should be changed before sending the message
258 sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
262 // show degrees of freedom
265 mySketchSolver->undo();
266 if (!myConstraints.empty()) {
267 // the error message should be changed before sending the message
268 const std::string& aErrorMsg = aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED ?
269 SketchSolver_Error::DEGENERATED_GEOMETRY() :
270 SketchSolver_Error::CONSTRAINTS();
271 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(aErrorMsg);
272 if (myPrevResult != aResult ||
273 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
274 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
275 // Obtain list of conflicting constraints
276 std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
278 if (!myConflictingConstraints.empty()) {
279 std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
280 for (; anIt != aConflicting.end(); ++anIt)
281 myConflictingConstraints.erase(*anIt);
282 if (!myConflictingConstraints.empty()) {
283 // some constraints does not conflict, send corresponding message
284 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
287 myConflictingConstraints = aConflicting;
288 if (!myConflictingConstraints.empty())
289 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
290 myPrevResult = aResult;
294 // show degrees of freedom only if the degenerated geometry appears
295 if (aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED)
300 else if (isGroupEmpty && isWorkplaneValid()) {
301 // clear error related to previously degenerated entities
302 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED) {
303 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
304 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
305 // the error message should be changed before sending the message
306 myConflictingConstraints.clear();
307 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
312 removeTemporaryConstraints();
313 myStorage->setNeedToResolve(false);
317 // ============================================================================
318 // Function: computeDoF
319 // Class: SketchSolver_Group
320 // Purpose: compute DoF of the sketch and set corresponding field
321 // ============================================================================
322 void SketchSolver_Group::computeDoF()
324 std::ostringstream aDoFMsg;
325 int aDoF = mySketchSolver->dof();
326 /// "DoF = 0" content of string value is used in PartSet by Sketch edit
327 /// If it is changed, it should be corrected also there
329 aDoFMsg << "Sketch is fully fixed (DoF = 0)";
331 aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
332 mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
334 if (aDoF > 0 && myDOF <= 0)
335 sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
336 else if (aDoF == 0/* && myDOF > 0*/)
337 sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
339 sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
344 // ============================================================================
345 // Function: repairConsistency
346 // Class: SketchSolver_Group
347 // Purpose: search removed entities and constraints
348 // ============================================================================
349 void SketchSolver_Group::repairConsistency()
351 if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
352 // remove invalid constraints
353 std::set<ConstraintPtr> anInvalidConstraints;
354 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
355 for (; aCIter != myConstraints.end(); ++aCIter) {
356 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
357 anInvalidConstraints.insert(aCIter->first);
359 std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
360 for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
361 removeConstraint(*aRemoveIt);
363 // remove invalid features
364 myStorage->removeInvalidEntities();
371 // ============================================================================
372 // Function: removeTemporaryConstraints
373 // Class: SketchSolver_Group
374 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
375 // resolving the set of constraints
376 // ============================================================================
377 void SketchSolver_Group::removeTemporaryConstraints()
379 if (!myTempConstraints.empty()) {
380 mySketchSolver->removeConstraint(CID_MOVEMENT);
382 std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
383 for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
386 myTempConstraints.clear();
389 myStorage->setNeedToResolve(false);
392 // ============================================================================
393 // Function: removeConstraint
394 // Class: SketchSolver_Group
395 // Purpose: remove constraint and all unused entities
396 // ============================================================================
397 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
399 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
400 for (; aCIter != myConstraints.end(); aCIter++)
401 if (aCIter->first == theConstraint) {
402 aCIter->second->remove(); // the constraint is not fully removed
404 // constraint is removed => reset stack of "multi" constraints updates
405 myMultiConstraintUpdateStack = 0;
408 if (aCIter != myConstraints.end())
409 myConstraints.erase(aCIter);
412 // ============================================================================
413 // Function: setTemporary
414 // Class: SketchSolver_Group
415 // Purpose: append given constraint to the group of temporary constraints
416 // ============================================================================
417 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
420 myTempConstraints.insert(theConstraint);
423 // ============================================================================
424 // Function: blockEvents
425 // Class: SketchSolver_Group
426 // Purpose: block or unblock events from features in this group
427 // ============================================================================
428 void SketchSolver_Group::blockEvents(bool isBlocked)
430 if (myIsEventsBlocked == isBlocked)
433 // block/unblock events from the features in the storage
434 myStorage->blockEvents(isBlocked);
436 // block/unblock events from constraints
437 ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
438 for (; aCIt != myConstraints.end(); ++aCIt)
439 aCIt->second->blockEvents(isBlocked);
441 myIsEventsBlocked = isBlocked;
444 bool SketchSolver_Group::areConstraintsValid() const
446 // Check the constraints are valid
447 ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin();
448 for (; aCIter != myConstraints.end(); ++aCIter)
449 if (!aCIter->first->data() || !aCIter->first->data()->isValid())