1 // Copyright (C) 2014-2019 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
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 : myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
78 myIsEventsBlocked(false),
79 myMultiConstraintUpdateStack(0)
81 mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
82 myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
83 updateSketch(theWorkplane);
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::updateSketch(CompositeFeaturePtr theSketch)
128 static const double THE_TOLERANCE = 1.e-7;
129 bool isChanged = theSketch != mySketch;
131 AttributePointPtr anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
132 theSketch->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
133 AttributeDirPtr aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
134 theSketch->attribute(SketchPlugin_Sketch::NORM_ID()));
135 AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
136 theSketch->attribute(SketchPlugin_Sketch::DIRX_ID()));
138 isChanged = isChanged
139 || (mySketchOrigin && anOrigin && anOrigin->pnt()->distance(mySketchOrigin) > THE_TOLERANCE)
140 || (mySketchNormal && aNorm && aNorm->xyz()->distance(mySketchNormal->xyz()) > THE_TOLERANCE)
141 || (mySketchXDir && aDirX && aDirX->xyz()->distance(mySketchXDir->xyz()) > THE_TOLERANCE);
144 mySketch = theSketch;
145 mySketchOrigin = anOrigin->pnt();
146 mySketchNormal = aNorm->dir();
147 mySketchXDir = aDirX->dir();
149 myStorage->notify(theSketch);
154 bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
156 return myStorage->update(theFeature);
159 template <class Type>
160 static SolverConstraintPtr move(StoragePtr theStorage,
161 SolverPtr theSketchSolver,
163 bool theEventsBlocked,
164 Type theFeatureOrPoint,
165 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
166 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
168 bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
169 if (theSketchDOF == 0 && isEntityExists) {
170 // avoid moving elements of fully constrained sketch
171 theStorage->refresh();
172 return SolverConstraintPtr();
175 // Create temporary Fixed constraint
176 std::shared_ptr<SketchSolver_ConstraintMovement> aConstraint =
177 PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint);
179 SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
180 if (aConstraint->error().empty()) {
181 aConstraint->startPoint(theFrom);
182 theStorage->adjustParametrizationOfArcs();
183 theSketchSolver->initialize();
184 aConstraint->moveTo(theTo);
185 theStorage->setNeedToResolve(true);
187 theStorage->notify(aConstraint->movedFeature());
193 bool SketchSolver_Group::moveFeature(FeaturePtr theFeature,
194 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
195 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
197 SolverConstraintPtr aConstraint =
198 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
199 setTemporary(aConstraint);
203 bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
204 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
205 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
207 SolverConstraintPtr aConstraint =
208 move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
209 setTemporary(aConstraint);
213 // ============================================================================
214 // Function: resolveConstraints
215 // Class: SketchSolver_Group
216 // Purpose: solve the set of constraints for the current group
217 // ============================================================================
218 bool SketchSolver_Group::resolveConstraints()
220 static const int MAX_STACK_SIZE = 5;
221 // check the "Multi" constraints do not drop sketch into infinite loop
222 if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) {
223 myMultiConstraintUpdateStack = 0;
224 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
225 // generate error message due to loop update of the sketch
226 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
227 ->setValue(SketchSolver_Error::INFINITE_LOOP());
228 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
232 bool aResolved = false;
233 bool isGroupEmpty = isEmpty() && myStorage->isEmpty();
234 if (myStorage->isNeedToResolve() &&
235 (!isGroupEmpty || !myConflictingConstraints.empty() ||
236 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
238 PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
241 myStorage->adjustParametrizationOfArcs();
242 aResult = mySketchSolver->solve();
244 if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
245 !myTempConstraints.empty()) {
246 mySketchSolver->undo();
247 removeTemporaryConstraints();
248 aResult = mySketchSolver->solve();
250 // check degenerated geometry after constraints resolving
251 if (aResult == PlaneGCSSolver_Solver::STATUS_OK)
252 aResult = myStorage->checkDegeneratedGeometry();
254 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
255 ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
256 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK ||
257 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
258 // the error message should be changed before sending the message
259 sendMessage(EVENT_SOLVER_FAILED);
260 myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED;
262 mySketchSolver->undo();
265 // solution succeeded, store results into correspondent attributes
266 if (aResult == PlaneGCSSolver_Solver::STATUS_OK ||
267 aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) {
268 myStorage->setNeedToResolve(false);
269 myStorage->refresh();
271 // additional check that copied entities used in Mirror and other "Multi" constraints
272 // is not connected with their originals by constraints.
273 myMultiConstraintUpdateStack += 1;
275 if (myStorage->isNeedToResolve())
276 aResolved = resolveConstraints();
279 myMultiConstraintUpdateStack -= 1;
281 if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK ||
282 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) {
283 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
284 std::set<ObjectPtr> aConflicting = myConflictingConstraints;
285 myConflictingConstraints.clear();
286 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
287 // the error message should be changed before sending the message
288 sendMessage(EVENT_SOLVER_REPAIRED, aConflicting);
292 // show degrees of freedom
295 mySketchSolver->undo();
296 if (!myConstraints.empty()) {
297 // the error message should be changed before sending the message
298 const std::string& aErrorMsg = aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED ?
299 SketchSolver_Error::DEGENERATED_GEOMETRY() :
300 SketchSolver_Error::CONSTRAINTS();
301 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(aErrorMsg);
302 if (myPrevResult != aResult ||
303 myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
304 myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
305 // Obtain list of conflicting constraints
306 std::set<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
308 if (!myConflictingConstraints.empty()) {
309 std::set<ObjectPtr>::iterator anIt = aConflicting.begin();
310 for (; anIt != aConflicting.end(); ++anIt)
311 myConflictingConstraints.erase(*anIt);
312 if (!myConflictingConstraints.empty()) {
313 // some constraints does not conflict, send corresponding message
314 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
317 myConflictingConstraints = aConflicting;
318 if (!myConflictingConstraints.empty())
319 sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints);
320 myPrevResult = aResult;
324 // show degrees of freedom only if the degenerated geometry appears
325 if (aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED)
330 else if (isGroupEmpty && isWorkplaneValid()) {
331 // clear error related to previously degenerated entities
332 if (myPrevResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED) {
333 getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
334 myPrevResult = PlaneGCSSolver_Solver::STATUS_OK;
335 // the error message should be changed before sending the message
336 myConflictingConstraints.clear();
337 sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints);
342 removeTemporaryConstraints();
343 myStorage->setNeedToResolve(false);
347 // ============================================================================
348 // Function: computeDoF
349 // Class: SketchSolver_Group
350 // Purpose: compute DoF of the sketch and set corresponding field
351 // ============================================================================
352 void SketchSolver_Group::computeDoF()
354 std::ostringstream aDoFMsg;
355 int aDoF = mySketchSolver->dof();
356 /// "DoF = 0" content of string value is used in PartSet by Sketch edit
357 /// If it is changed, it should be corrected also there
359 aDoFMsg << "Sketch is fully fixed (DoF = 0)";
361 aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
362 mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str());
364 if (aDoF > 0 && myDOF <= 0)
365 sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF);
366 else if (aDoF == 0 && myDOF != 0)
367 sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
369 sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
374 // ============================================================================
375 // Function: repairConsistency
376 // Class: SketchSolver_Group
377 // Purpose: search removed entities and constraints
378 // ============================================================================
379 void SketchSolver_Group::repairConsistency()
381 if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
382 // remove invalid constraints
383 std::set<ConstraintPtr> anInvalidConstraints;
384 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
385 for (; aCIter != myConstraints.end(); ++aCIter) {
386 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
387 anInvalidConstraints.insert(aCIter->first);
389 std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
390 for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
391 removeConstraint(*aRemoveIt);
393 // remove invalid features
394 myStorage->removeInvalidEntities();
401 // ============================================================================
402 // Function: removeTemporaryConstraints
403 // Class: SketchSolver_Group
404 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
405 // resolving the set of constraints
406 // ============================================================================
407 void SketchSolver_Group::removeTemporaryConstraints()
409 if (!myTempConstraints.empty()) {
410 mySketchSolver->removeConstraint(CID_MOVEMENT);
412 std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
413 for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
416 myTempConstraints.clear();
419 myStorage->setNeedToResolve(false);
422 // ============================================================================
423 // Function: removeConstraint
424 // Class: SketchSolver_Group
425 // Purpose: remove constraint and all unused entities
426 // ============================================================================
427 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
429 ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
430 for (; aCIter != myConstraints.end(); aCIter++)
431 if (aCIter->first == theConstraint) {
432 aCIter->second->remove(); // the constraint is not fully removed
434 // constraint is removed => reset stack of "multi" constraints updates
435 myMultiConstraintUpdateStack = 0;
438 if (aCIter != myConstraints.end())
439 myConstraints.erase(aCIter);
442 // ============================================================================
443 // Function: setTemporary
444 // Class: SketchSolver_Group
445 // Purpose: append given constraint to the group of temporary constraints
446 // ============================================================================
447 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
450 myTempConstraints.insert(theConstraint);
453 // ============================================================================
454 // Function: blockEvents
455 // Class: SketchSolver_Group
456 // Purpose: block or unblock events from features in this group
457 // ============================================================================
458 void SketchSolver_Group::blockEvents(bool isBlocked)
460 if (myIsEventsBlocked == isBlocked)
463 // block/unblock events from the features in the storage
464 myStorage->blockEvents(isBlocked);
466 // block/unblock events from constraints
467 ConstraintConstraintMap::iterator aCIt = myConstraints.begin();
468 for (; aCIt != myConstraints.end(); ++aCIt)
469 aCIt->second->blockEvents(isBlocked);
471 myIsEventsBlocked = isBlocked;
474 bool SketchSolver_Group::areConstraintsValid() const
476 // Check the constraints are valid
477 ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin();
478 for (; aCIter != myConstraints.end(); ++aCIter)
479 if (!aCIter->first->data() || !aCIter->first->data()->isValid())