1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_Manager.cpp
4 // Created: 08 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_Manager.h"
8 #include "SketchSolver_Error.h"
10 #include <Events_Loop.h>
12 #include <GeomDataAPI_Point2D.h>
14 #include <ModelAPI_AttributeDouble.h>
15 #include <ModelAPI_AttributeRefList.h>
16 #include <ModelAPI_Data.h>
17 #include <ModelAPI_Events.h>
18 #include <ModelAPI_Object.h>
19 #include <ModelAPI_ResultConstruction.h>
20 #include <ModelAPI_Attribute.h>
21 #include <ModelAPI_AttributeInteger.h>
22 #include <ModelAPI_AttributeString.h>
24 #include <SketchPlugin_Constraint.h>
25 #include <SketchPlugin_ConstraintAngle.h>
26 #include <SketchPlugin_ConstraintCoincidence.h>
27 #include <SketchPlugin_ConstraintCollinear.h>
28 #include <SketchPlugin_ConstraintDistance.h>
29 #include <SketchPlugin_ConstraintEqual.h>
30 #include <SketchPlugin_ConstraintHorizontal.h>
31 #include <SketchPlugin_ConstraintLength.h>
32 #include <SketchPlugin_ConstraintMiddle.h>
33 #include <SketchPlugin_ConstraintMirror.h>
34 #include <SketchPlugin_ConstraintParallel.h>
35 #include <SketchPlugin_ConstraintPerpendicular.h>
36 #include <SketchPlugin_ConstraintRadius.h>
37 #include <SketchPlugin_ConstraintRigid.h>
38 #include <SketchPlugin_ConstraintTangent.h>
39 #include <SketchPlugin_ConstraintVertical.h>
40 #include <SketchPlugin_MultiRotation.h>
41 #include <SketchPlugin_MultiTranslation.h>
43 #include <SketchPlugin_Arc.h>
44 #include <SketchPlugin_Circle.h>
45 #include <SketchPlugin_Line.h>
46 #include <SketchPlugin_Point.h>
47 #include <SketchPlugin_Sketch.h>
48 #include <SketchPlugin_Feature.h>
56 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
58 // Initialization of constraint manager self pointer
59 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
61 /// Global constraint manager object
62 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
65 // ========================================================
66 // ========= SketchSolver_Manager ===============
67 // ========================================================
68 SketchSolver_Manager* SketchSolver_Manager::instance()
71 mySelf = new SketchSolver_Manager();
75 SketchSolver_Manager::SketchSolver_Manager()
80 // Register in event loop
81 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
82 Events_Loop::loop()->registerListener(this, anUpdateEvent);
83 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
84 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
86 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
87 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
88 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SKETCH_PREPARED));
91 SketchSolver_Manager::~SketchSolver_Manager()
96 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
98 myBuilder = theBuilder;
101 BuilderPtr SketchSolver_Manager::builder()
106 bool SketchSolver_Manager::groupMessages()
111 // ============================================================================
112 // Function: processEvent
113 // Purpose: listen the event loop and process the message
114 // ============================================================================
115 void SketchSolver_Manager::processEvent(
116 const std::shared_ptr<Events_Message>& theMessage)
118 static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
119 // sketch is prepared for resolve: all the needed events
120 // are collected and must be processed by the solver
121 if (theMessage->eventID() == aSketchPreparedEvent) {
122 flushGrouped(anUpdateEvent);
126 checkConflictingConstraints(theMessage);
132 // Shows that the message has at least one feature applicable for solver
133 bool hasProperFeature = false;
135 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
136 || theMessage->eventID() == anUpdateEvent
137 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
138 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
139 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
140 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
142 bool isUpdateFlushed = stopSendUpdate();
144 bool isMovedEvt = theMessage->eventID()
145 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
147 std::set<ObjectPtr>::iterator aFeatIter;
148 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
149 std::shared_ptr<SketchPlugin_Feature> aSFeature =
150 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
151 if (aSFeature && moveEntity(aSFeature)) {
152 // Want to avoid recalculation of DoF too frequently.
153 // So, set the flag when the feature is really moved.
154 hasProperFeature = true;
157 if (!hasProperFeature)
158 // in this iteration it will compute nothing, so, no problem with recursion
159 // it is important that solver flushes signal updated after processing move signal as there
160 // is optimization that relies on this update, might be found by key "optimization"
161 myIsComputed = false;
163 std::list<FeaturePtr> aSketchFeatures =
164 SketchSolver_Group::selectApplicableFeatures(aFeatures);
165 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
166 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
167 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
168 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
169 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
170 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
173 std::shared_ptr<SketchPlugin_Feature> aFeature =
174 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
177 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
181 bool needToUpdate = false;
182 // Solve the set of constraints
183 if (hasProperFeature)
184 needToUpdate = resolveConstraints();
186 // Features may be updated => now send events, but for all changed at once
190 myIsComputed = false;
192 // send update for movement in any case
193 if (needToUpdate || isMovedEvt)
194 Events_Loop::loop()->flush(anUpdateEvent);
196 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
197 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
198 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
199 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
201 // Find SketchPlugin_Sketch::ID() in groups.
202 // The constraint groups should be updated when an object removed from Sketch
203 std::set<std::string>::const_iterator aFGrIter;
204 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
205 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
206 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
209 if (aFGrIter != aFeatureGroups.end()) {
210 hasProperFeature = true;
211 std::list<SketchSolver_Group*> aGroupsToResolve;
212 std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
213 std::list<SketchSolver_Group*> aSeparatedGroups;
214 while (aGroupIter != myGroups.end()) {
215 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
217 std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
218 myGroups.erase(aRemoveIt);
221 if (!(*aGroupIter)->isConsistent()) {
222 // some constraints were removed, try to split the group
223 (*aGroupIter)->splitGroup(aSeparatedGroups);
224 if (!(*aGroupIter)->getWorkplane()->string(
225 SketchPlugin_Sketch::SOLVER_ERROR())->value().empty() ||
226 (*aGroupIter)->isFailed())
227 aGroupsToResolve.push_back(*aGroupIter);
231 if (aSeparatedGroups.size() > 0) {
232 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
233 aGroupsToResolve.insert(aGroupsToResolve.end(),
234 aSeparatedGroups.begin(), aSeparatedGroups.end());
237 if (!aGroupsToResolve.empty())
238 resolveConstraints(aGroupsToResolve);
240 myIsComputed = false;
243 if (hasProperFeature)
247 void SketchSolver_Manager::
248 checkConflictingConstraints(const std::shared_ptr<Events_Message>& theMessage)
250 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) {
251 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
252 std::dynamic_pointer_cast<ModelAPI_SolverFailedMessage>(theMessage);
253 std::set<ObjectPtr> aSentObjs = aMessage->objects();
254 if (!aSentObjs.empty()) {
255 // Obtain sketch where the constraints are placed.
256 // It is enough to check only one constraint.
257 CompositeFeaturePtr aSketch;
258 FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin());
259 std::list<SketchSolver_Group*>::const_iterator aGrIt = myGroups.begin();
260 for (; aGrIt != myGroups.end(); ++aGrIt)
261 if ((*aGrIt)->isInteract(aConstraint)) {
262 aSketch = (*aGrIt)->getWorkplane();
266 // Search failed groups built on the same sketch
268 for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) {
269 SketchSolver_Group* aGroup = *aGrIt;
270 if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() &&
271 !aGroup->isInteract(aConstraint)) {
272 // reset error message on the sketch
273 aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(
274 SketchSolver_Error::CONSTRAINTS());
283 // ============================================================================
284 // Function: changeWorkplane
285 // Purpose: update workplane by given parameters of the sketch
286 // ============================================================================
287 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
289 bool aResult = true; // changed when a workplane wrongly updated
290 bool isUpdated = false;
291 // Try to update specified workplane in all groups
292 std::list<SketchSolver_Group*>::iterator aGroupIter;
293 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
294 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
298 // If the workplane is not updated, so this is a new workplane
300 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
301 // Verify that the group is created successfully
302 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
306 myGroups.push_back(aNewGroup);
308 return aResult || isUpdated;
311 // ============================================================================
312 // Function: changeConstraintOrEntity
313 // Purpose: create/update the constraint or the feature and place it into appropriate group
314 // ============================================================================
315 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
317 // Search the groups which this feature touches
318 std::set<GroupID> aGroups;
319 findGroups(theFeature, aGroups);
321 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
322 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
324 // Process the groups list
325 if (aGroups.size() == 0) {
326 // There are no groups applicable for this constraint => create new one
327 // The group will be created only for constraints, not for features
328 if (!aConstraint) return false;
329 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
332 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
333 if (!aGroup->changeConstraint(aConstraint)) {
337 myGroups.push_back(aGroup);
339 } else if (aGroups.size() == 1) { // Only one group => add feature into it
340 GroupID aGroupId = *(aGroups.begin());
341 std::list<SketchSolver_Group*>::iterator aGroupIter;
342 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
343 if ((*aGroupIter)->getId() == aGroupId) {
344 // If the group is empty, the feature is not added (the constraint only)
345 if (!aConstraint && !(*aGroupIter)->isEmpty())
346 return (*aGroupIter)->updateFeature(theFeature);
347 return (*aGroupIter)->changeConstraint(aConstraint);
349 } else if (aGroups.size() > 1) {
350 // Several groups applicable for this feature => need to merge them
351 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
353 // Search first group
354 std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
355 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
356 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
358 if (aFirstGroupIter == myGroups.end())
361 // Append other groups to the first one
362 std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
364 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
365 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
366 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
368 if (anOtherGroupIter == myGroups.end()) { // Group disappears
369 anOtherGroupIter = aFirstGroupIter;
374 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
375 std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
377 myGroups.erase(aRemoveIt);
381 (*aFirstGroupIter)->changeConstraint(aConstraint);
383 (*aFirstGroupIter)->updateFeature(theFeature);
384 // groups are merged => need to resolve them
388 // Something goes wrong
392 // ============================================================================
393 // Function: moveEntity
394 // Purpose: update element moved on the sketch, which is used by constraints
395 // ============================================================================
396 bool SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
398 bool isMoved = false;
399 std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
400 for (; aGroupIt != myGroups.end(); aGroupIt++)
401 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
402 isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
404 if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
405 // Workaround to move arc.
406 // If the arc has not been constrained, we will push it into empty group and apply movement.
407 bool hasEmptyGroup = false;
408 for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
409 if ((*aGroupIt)->isEmpty()) {
410 isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
411 hasEmptyGroup = true;
413 // There is no empty group, create it explicitly
414 if (!hasEmptyGroup) {
415 // find sketch containing the arc
416 CompositeFeaturePtr aWP;
417 const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
418 std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
419 for (; aRefIt != aRefs.end(); ++aRefIt) {
420 FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
421 if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
422 aWP = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
427 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
428 isMoved = aGroup->moveFeature(theFeature) || isMoved;
429 myGroups.push_back(aGroup);
436 // ============================================================================
437 // Function: findGroups
438 // Purpose: search groups of entities interacting with given feature
439 // ============================================================================
440 void SketchSolver_Manager::findGroups(
441 std::shared_ptr<SketchPlugin_Feature> theFeature,
442 std::set<GroupID>& theGroupIDs) const
444 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
446 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
447 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
448 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
449 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
450 if (!(*aGroupIter)->isEmpty())
451 theGroupIDs.insert((*aGroupIter)->getId());
452 else if (!anEmptyGroup)
453 anEmptyGroup = *aGroupIter;
456 // When only empty group is found, use it
457 if (anEmptyGroup && theGroupIDs.empty())
458 theGroupIDs.insert(anEmptyGroup->getId());
461 // ============================================================================
462 // Function: findWorkplane
463 // Purpose: search workplane containing given feature
464 // ============================================================================
465 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
466 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
468 // Already verified workplanes
469 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
471 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
472 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
473 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
474 if (aVerified.find(aWP) != aVerified.end())
477 DataPtr aData = aWP->data();
478 if (aData->isValid()) {
479 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
480 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
481 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
482 std::list<ObjectPtr>::const_iterator anIter;
483 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
484 if (*anIter == theFeature)
485 return aWP; // workplane is found
487 aVerified.insert(aWP);
490 return std::shared_ptr<ModelAPI_CompositeFeature>();
493 // ============================================================================
494 // Function: resolveConstraints
495 // Purpose: change entities according to available constraints
496 // ============================================================================
497 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
499 bool needToUpdate = false;
500 const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ?
501 myGroups : theGroups;
502 std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
503 for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
504 if ((*aGroupIter)->resolveConstraints())
510 // Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints
511 static void collectPointsAndCopies(FeaturePtr theConstraint,
512 std::list<std::set<AttributePtr> >& thePoints)
514 typedef std::list<std::string> strlist;
515 static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID());
516 static strlist aLineAttributes;
517 if (aLineAttributes.empty()) {
518 aLineAttributes.push_back(SketchPlugin_Line::START_ID());
519 aLineAttributes.push_back(SketchPlugin_Line::END_ID());
521 static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID());
522 static strlist anArcAttributes;
523 if (anArcAttributes.empty()) {
524 anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID());
525 anArcAttributes.push_back(SketchPlugin_Arc::START_ID());
526 anArcAttributes.push_back(SketchPlugin_Arc::END_ID());
529 static std::map<std::string, strlist> aFeatureAttributes;
530 if (aFeatureAttributes.empty()) {
531 aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes;
532 aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes;
533 aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes;
534 aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes;
538 std::set<AttributePtr> aPoints;
539 if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
540 AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
541 AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C());
543 std::list<ObjectPtr> aBaseList = aBaseRefList->list();
544 std::list<ObjectPtr> aMirrList = aMirrRefList->list();
545 std::list<ObjectPtr>::const_iterator aBIt, aMIt;
546 for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin();
547 aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) {
548 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt);
549 FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt);
551 strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
552 strlist::iterator anIt = anAttrList.begin();
553 for (; anIt != anAttrList.end(); ++anIt) {
555 aPoints.insert(aBaseFeature->attribute(*anIt));
556 aPoints.insert(aMirrFeature->attribute(*anIt));
557 thePoints.push_back(aPoints);
561 else { // the "Multi" constraints
562 std::string aNbObjName;
563 if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID())
564 aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID();
566 aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID();
567 int aNbCopies = theConstraint->integer(aNbObjName)->value();
569 AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
570 std::list<ObjectPtr> aFullList = aRefList->list();
571 std::list<ObjectPtr>::const_iterator anObjIt = aFullList.begin();
572 std::list<ObjectPtr>::const_iterator aCopyIt;
573 while (anObjIt != aFullList.end()) {
574 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt);
575 strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
576 strlist::iterator anIt = anAttrList.begin();
577 for (; anIt != anAttrList.end(); ++anIt) {
580 for (int i = 0; i < aNbCopies && aCopyIt != aFullList.end(); ++i, ++aCopyIt) {
581 FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt);
582 aPoints.insert(aFeature->attribute(*anIt));
584 thePoints.push_back(aPoints);
592 // returns true if the feature is external
593 static bool isExternal(const FeaturePtr& theFeature)
595 AttributeSelectionPtr anAttr = theFeature->selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
596 return anAttr && anAttr->context() && !anAttr->isInvalid();
599 // ============================================================================
600 // Function: degreesOfFreedom
601 // Purpose: calculate DoFs for each sketch
602 // ============================================================================
603 void SketchSolver_Manager::degreesOfFreedom()
605 static std::map<std::string, int> aDoFDelta; // indicates how many DoF adds or decreases a feature
606 static bool isNeedInit = true;
608 aDoFDelta[SketchPlugin_Point::ID()] = 2;
609 aDoFDelta[SketchPlugin_Line::ID()] = 4;
610 aDoFDelta[SketchPlugin_Circle::ID()] = 3;
611 aDoFDelta[SketchPlugin_Arc::ID()] = 5;
613 aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1;
614 aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1;
615 aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1;
616 aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1;
617 aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1;
618 aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1;
619 aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1;
620 aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1;
621 aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1;
622 aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1;
623 aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1;
628 std::map<CompositeFeaturePtr, int> aSketchDoF;
630 std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
631 for (; aGroupIt != myGroups.end(); ++aGroupIt) {
632 CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
633 bool isSketchValid = aSketch->data() && aSketch->data()->isValid();
636 std::shared_ptr<GeomDataAPI_Dir> aNormal =
637 std::dynamic_pointer_cast<GeomDataAPI_Dir>(
638 aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
639 isSketchValid = aNormal && aNormal->isInitialized();
642 if (!isSketchValid) {
643 myDoF.erase(aSketch);
647 // check conflicting constraints in the group
648 if ((*aGroupIt)->isFailed())
649 aSketchDoF[aSketch] = -1;
650 // check the sketch is already processed
651 if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
654 std::set<AttributePtr> aCoincidentPoints;
655 std::set<AttributePtr> aFixedPoints;
656 std::map<AttributePtr, std::set<FeaturePtr> > aPointOnLine;
657 std::list<std::set<AttributePtr> > aPointsInMultiConstraints;
659 int aNbSubs = aSketch->numberOfSubs();
660 for (int i = 0; i < aNbSubs; ++i) {
661 FeaturePtr aFeature = aSketch->subFeature(i);
662 // do not change DoF for external feature
663 if (isExternal(aFeature))
665 // check DoF delta for invariant types
666 std::map<std::string, int>::const_iterator aFound = aDoFDelta.find(aFeature->getKind());
667 if (aFound != aDoFDelta.end()) {
668 aDoF += aFound->second;
672 // DoF delta in specific cases
673 if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
674 AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
675 FeaturePtr aCoincLine;
676 for (int j = 0; j < 2; ++j) {
677 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
678 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
681 if (!aRefAttr->isObject())
682 aCoincPoint[j] = aRefAttr->attr();
684 FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
687 if (anAttr->getKind() == SketchPlugin_Point::ID())
688 aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
689 else if (anAttr->getKind() == SketchPlugin_Line::ID())
693 if (aCoincPoint[0] && aCoincPoint[1]) {
694 bool isDoFDecreased = false;
695 // point-point coincidence
696 if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() ||
697 aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end()) {
699 isDoFDecreased = true;
701 // check the coincident point is used in "multi" constraints
702 std::list<std::set<AttributePtr> >::const_iterator
703 aPtIt = aPointsInMultiConstraints.begin();
704 bool isFound[2] = {false, false};
705 for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) {
706 if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end())))
707 || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end()))))
708 aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end());
709 if (isFound[0] && isFound[1])
712 // check both points are fixed => not need to decrease DoF
713 bool isFixed[2] = { aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end(),
714 aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() };
715 if (isFixed[0] && isFixed[1] && isDoFDecreased)
716 aDoF += 2; // revert decrease of DoF
717 else if (isFixed[0] && !isFixed[1])
718 aFixedPoints.insert(aCoincPoint[1]);
719 else if (!isFixed[0] && isFixed[1])
720 aFixedPoints.insert(aCoincPoint[0]);
723 if (aCoincPoint[0] && aCoincLine) {
724 // if the point is already coincident to a line
725 // (by middle point constraint), do not decrease DoF
726 std::map<AttributePtr, std::set<FeaturePtr> >::iterator
727 aPtFound = aPointOnLine.find(aCoincPoint[0]);
728 if (aPtFound != aPointOnLine.end() &&
729 aPtFound->second.find(aCoincLine) != aPtFound->second.end())
730 aDoF += 1; // restore value decreased above
732 aPointOnLine[aCoincPoint[0]].insert(aCoincLine);
735 for (int j = 0; j < 2; ++j)
737 aCoincidentPoints.insert(aCoincPoint[j]);
739 else if (aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
742 for (int j = 0; j < 2; ++j) {
743 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
744 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
747 if (aRefAttr->isObject())
748 aLine = ModelAPI_Feature::feature(aRefAttr->object());
750 aPoint = aRefAttr->attr();
752 if (aPoint && aLine) {
753 // if the point is already on the line, decrease 1 DoF, instead decrease 2 DoF
754 std::map<AttributePtr, std::set<FeaturePtr> >::iterator
755 aPtFound = aPointOnLine.find(aPoint);
756 if (aPtFound != aPointOnLine.end() &&
757 aPtFound->second.find(aLine) != aPtFound->second.end())
761 aPointOnLine[aPoint].insert(aLine);
765 else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
766 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
767 aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
769 std::set<AttributePtr> aPoints;
770 if (!aRefAttr->isObject()) {
771 aDoF -= 2; // attribute is a point
772 aPoints.insert(aRefAttr->attr());
774 FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
776 if (isExternal(anAttr))
777 continue; // feature is already fixed since it is external
778 aDoF -= aDoFDelta[anAttr->getKind()];
779 std::list<AttributePtr> aPtAttrs =
780 anAttr->data()->attributes(GeomDataAPI_Point2D::typeId());
781 aPoints.insert(aPtAttrs.begin(), aPtAttrs.end());
785 // Check whether feature's points are already coincident with fixed points.
786 // In this case we need to revert decrease of DoF for these points.
787 // If the coordinates of fixed points are different, it will be processed by solver.
788 for (int k = 0; k < i; ++k) {
789 FeaturePtr aFeature = aSketch->subFeature(k);
790 if (aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
792 AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
793 for (int j = 0; j < 2; ++j) {
794 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
795 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
798 if (!aRefAttr->isObject())
799 aCoincPoint[j] = aRefAttr->attr();
801 FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
802 if (anAttr && anAttr->getKind() == SketchPlugin_Point::ID())
803 aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
806 if (aCoincPoint[0] && aCoincPoint[1]) {
807 if ((aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end() &&
808 aPoints.find(aCoincPoint[1]) != aPoints.end()) ||
809 (aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() &&
810 aPoints.find(aCoincPoint[0]) != aPoints.end()))
811 aDoF += 2; // point already fixed
814 // store fixed points
815 aFixedPoints.insert(aPoints.begin(), aPoints.end());
817 else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
818 aFeature->getKind() == SketchPlugin_MultiRotation::ID() ||
819 aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) {
821 std::string anAttrName;
822 if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID())
823 anAttrName = SketchPlugin_Constraint::ENTITY_B();
825 if (aFeature->getKind() == SketchPlugin_MultiRotation::ID())
827 aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1;
828 else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID())
830 aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1;
831 anAttrName = SketchPlugin_Constraint::ENTITY_A();
834 AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
835 aFeature->attribute(anAttrName));
836 std::list<ObjectPtr> anObjList = aRefListOfShapes->list();
837 std::list<ObjectPtr>::const_iterator anObjIt = anObjList.begin();
838 for (; anObjIt != anObjList.end(); ++anObjIt) {
839 FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
840 aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
842 // collect points and their copies for correct calculation of DoF for coincident points
843 collectPointsAndCopies(aFeature, aPointsInMultiConstraints);
847 aSketchDoF[aSketch] = aDoF;
850 // Check the degrees of freedom are changed
851 std::map<CompositeFeaturePtr, int>::const_iterator aDoFIt = aSketchDoF.begin();
852 std::map<CompositeFeaturePtr, int>::iterator aFound;
853 for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) {
854 if (aDoFIt->second < 0)
855 continue; // conflicting constraints on the current sketch
856 aFound = myDoF.find(aDoFIt->first);
857 if (aFound != myDoF.end() && aFound->second == aDoFIt->second)
858 continue; // nothing is changed
859 myDoF[aDoFIt->first] = aDoFIt->second;
860 // change attribute value
861 std::ostringstream aStream;
862 if (aDoFIt->second == 0)
863 aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")";
865 aStream << "DOF (degree of freedom) = " << aDoFIt->second;
866 aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str());
870 bool SketchSolver_Manager::stopSendUpdate() const
872 // to avoid redisplay of each segment on update by solver one by one in the viewer
873 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
874 if (isUpdateFlushed) {
875 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
877 return isUpdateFlushed;
880 void SketchSolver_Manager::allowSendUpdate() const
882 Events_Loop::loop()->setFlushed(anUpdateEvent, true);