+ const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ?
+ myGroups : theGroups;
+ std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
+ for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
+ if ((*aGroupIter)->resolveConstraints())
+ needToUpdate = true;
+ return needToUpdate;
+}
+
+
+// Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints
+static void collectPointsAndCopies(FeaturePtr theConstraint,
+ std::list<std::set<AttributePtr> >& thePoints)
+{
+ typedef std::list<std::string> strlist;
+ static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID());
+ static strlist aLineAttributes;
+ if (aLineAttributes.empty()) {
+ aLineAttributes.push_back(SketchPlugin_Line::START_ID());
+ aLineAttributes.push_back(SketchPlugin_Line::END_ID());
+ };
+ static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID());
+ static strlist anArcAttributes;
+ if (anArcAttributes.empty()) {
+ anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID());
+ anArcAttributes.push_back(SketchPlugin_Arc::START_ID());
+ anArcAttributes.push_back(SketchPlugin_Arc::END_ID());
+ };
+
+ static std::map<std::string, strlist> aFeatureAttributes;
+ if (aFeatureAttributes.empty()) {
+ aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes;
+ aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes;
+ aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes;
+ aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes;
+ }
+
+
+ std::set<AttributePtr> aPoints;
+ if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
+ AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
+ AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C());
+
+ std::list<ObjectPtr> aBaseList = aBaseRefList->list();
+ std::list<ObjectPtr> aMirrList = aMirrRefList->list();
+ std::list<ObjectPtr>::const_iterator aBIt, aMIt;
+ for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin();
+ aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) {
+ FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt);
+ FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt);
+
+ strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
+ strlist::iterator anIt = anAttrList.begin();
+ for (; anIt != anAttrList.end(); ++anIt) {
+ aPoints.clear();
+ aPoints.insert(aBaseFeature->attribute(*anIt));
+ aPoints.insert(aMirrFeature->attribute(*anIt));
+ thePoints.push_back(aPoints);
+ }
+ }
+ }
+ else { // the "Multi" constraints
+ std::string aNbObjName;
+ if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID())
+ aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID();
+ else
+ aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID();
+ int aNbCopies = theConstraint->integer(aNbObjName)->value();
+
+ AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
+ std::list<ObjectPtr> aFullList = aRefList->list();
+ std::list<ObjectPtr>::const_iterator anObjIt = aFullList.begin();
+ std::list<ObjectPtr>::const_iterator aCopyIt;
+ while (anObjIt != aFullList.end()) {
+ FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt);
+ strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
+ strlist::iterator anIt = anAttrList.begin();
+ for (; anIt != anAttrList.end(); ++anIt) {
+ aPoints.clear();
+ aCopyIt = anObjIt;
+ for (int i = 0; i < aNbCopies && aCopyIt != aFullList.end(); ++i, ++aCopyIt) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt);
+ aPoints.insert(aFeature->attribute(*anIt));
+ }
+ thePoints.push_back(aPoints);
+ }
+ anObjIt = aCopyIt;
+ }
+ }
+}
+
+
+// returns true if the feature is external
+static bool isExternal(const FeaturePtr& theFeature)
+{
+ AttributeSelectionPtr anAttr = theFeature->selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
+ return anAttr && anAttr->context() && !anAttr->isInvalid();
+}
+
+// ============================================================================
+// Function: degreesOfFreedom
+// Purpose: calculate DoFs for each sketch
+// ============================================================================
+void SketchSolver_Manager::degreesOfFreedom()
+{
+ static std::map<std::string, int> aDoFDelta; // indicates how many DoF adds or decreases a feature
+ static bool isNeedInit = true;
+ if (isNeedInit) {
+ aDoFDelta[SketchPlugin_Point::ID()] = 2;
+ aDoFDelta[SketchPlugin_Line::ID()] = 4;
+ aDoFDelta[SketchPlugin_Circle::ID()] = 3;
+ aDoFDelta[SketchPlugin_Arc::ID()] = 5;
+
+ aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1;
+ aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1;
+
+ isNeedInit = false;
+ }
+
+ std::map<CompositeFeaturePtr, int> aSketchDoF;
+
+ std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
+ for (; aGroupIt != myGroups.end(); ++aGroupIt) {
+ CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
+ bool isSketchValid = aSketch->data() && aSketch->data()->isValid();
+
+ if (isSketchValid) {
+ std::shared_ptr<GeomDataAPI_Dir> aNormal =
+ std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+ aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
+ isSketchValid = aNormal && aNormal->isInitialized();
+ }
+
+ if (!isSketchValid) {
+ myDoF.erase(aSketch);
+ continue;
+ }
+
+ // check conflicting constraints in the group
+ if ((*aGroupIt)->isFailed())
+ aSketchDoF[aSketch] = -1;
+ // check the sketch is already processed
+ if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
+ continue;
+
+ std::set<AttributePtr> aCoincidentPoints;
+ std::set<AttributePtr> aFixedPoints;
+ std::map<AttributePtr, std::set<FeaturePtr> > aPointOnLine;
+ std::list<std::set<AttributePtr> > aPointsInMultiConstraints;
+ int aDoF = 0;
+ int aNbSubs = aSketch->numberOfSubs();
+ for (int i = 0; i < aNbSubs; ++i) {
+ FeaturePtr aFeature = aSketch->subFeature(i);
+ // do not change DoF for external feature
+ if (isExternal(aFeature))
+ continue;
+ // check DoF delta for invariant types
+ std::map<std::string, int>::const_iterator aFound = aDoFDelta.find(aFeature->getKind());
+ if (aFound != aDoFDelta.end()) {
+ aDoF += aFound->second;
+ continue;
+ }
+
+ // DoF delta in specific cases
+ if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
+ AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
+ FeaturePtr aCoincLine;
+ for (int j = 0; j < 2; ++j) {
+ AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+ aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
+ if (!aRefAttr)
+ continue;
+ if (!aRefAttr->isObject())
+ aCoincPoint[j] = aRefAttr->attr();
+ else {
+ FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
+ if (!anAttr)
+ continue;
+ if (anAttr->getKind() == SketchPlugin_Point::ID())
+ aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
+ else if (anAttr->getKind() == SketchPlugin_Line::ID())
+ aCoincLine = anAttr;
+ }
+ }
+ if (aCoincPoint[0] && aCoincPoint[1]) {
+ bool isDoFDecreased = false;
+ // point-point coincidence
+ if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() ||
+ aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end()) {
+ aDoF -= 2;
+ isDoFDecreased = true;
+ }
+ // check the coincident point is used in "multi" constraints
+ std::list<std::set<AttributePtr> >::const_iterator
+ aPtIt = aPointsInMultiConstraints.begin();
+ bool isFound[2] = {false, false};
+ for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) {
+ if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end())))
+ || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end()))))
+ aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end());
+ if (isFound[0] && isFound[1])
+ break;
+ }
+ // check both points are fixed => not need to decrease DoF
+ bool isFixed[2] = { aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end(),
+ aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() };
+ if (isFixed[0] && isFixed[1] && isDoFDecreased)
+ aDoF += 2; // revert decrease of DoF
+ else if (isFixed[0] && !isFixed[1])
+ aFixedPoints.insert(aCoincPoint[1]);
+ else if (!isFixed[0] && isFixed[1])
+ aFixedPoints.insert(aCoincPoint[0]);
+ } else {
+ aDoF -= 1;
+ if (aCoincPoint[0] && aCoincLine) {
+ // if the point is already coincident to a line
+ // (by middle point constraint), do not decrease DoF
+ std::map<AttributePtr, std::set<FeaturePtr> >::iterator
+ aPtFound = aPointOnLine.find(aCoincPoint[0]);
+ if (aPtFound != aPointOnLine.end() &&
+ aPtFound->second.find(aCoincLine) != aPtFound->second.end())
+ aDoF += 1; // restore value decreased above
+ else
+ aPointOnLine[aCoincPoint[0]].insert(aCoincLine);
+ }
+ }
+ for (int j = 0; j < 2; ++j)
+ if (aCoincPoint[j])
+ aCoincidentPoints.insert(aCoincPoint[j]);
+ }
+ else if (aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
+ AttributePtr aPoint;
+ FeaturePtr aLine;
+ for (int j = 0; j < 2; ++j) {
+ AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+ aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
+ if (!aRefAttr)
+ continue;
+ if (aRefAttr->isObject())
+ aLine = ModelAPI_Feature::feature(aRefAttr->object());
+ else
+ aPoint = aRefAttr->attr();
+ }
+ if (aPoint && aLine) {
+ // if the point is already on the line, decrease 1 DoF, instead decrease 2 DoF
+ std::map<AttributePtr, std::set<FeaturePtr> >::iterator
+ aPtFound = aPointOnLine.find(aPoint);
+ if (aPtFound != aPointOnLine.end() &&
+ aPtFound->second.find(aLine) != aPtFound->second.end())
+ aDoF -= 1;
+ else {
+ aDoF -= 2;
+ aPointOnLine[aPoint].insert(aLine);
+ }
+ }
+ }
+ else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
+ AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+ aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
+ assert(aRefAttr);
+ std::set<AttributePtr> aPoints;
+ if (!aRefAttr->isObject()) {
+ aDoF -= 2; // attribute is a point
+ aPoints.insert(aRefAttr->attr());
+ } else {
+ FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
+ if (anAttr) {
+ if (isExternal(anAttr))
+ continue; // feature is already fixed since it is external
+ aDoF -= aDoFDelta[anAttr->getKind()];
+ std::list<AttributePtr> aPtAttrs =
+ anAttr->data()->attributes(GeomDataAPI_Point2D::typeId());
+ aPoints.insert(aPtAttrs.begin(), aPtAttrs.end());
+ }
+ }
+
+ // Check whether feature's points are already coincident with fixed points.
+ // In this case we need to revert decrease of DoF for these points.
+ // If the coordinates of fixed points are different, it will be processed by solver.
+ for (int k = 0; k < i; ++k) {
+ FeaturePtr aFeature = aSketch->subFeature(k);
+ if (aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
+ continue;
+ AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
+ for (int j = 0; j < 2; ++j) {
+ AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+ aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
+ if (!aRefAttr)
+ continue;
+ if (!aRefAttr->isObject())
+ aCoincPoint[j] = aRefAttr->attr();
+ else {
+ FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
+ if (anAttr && anAttr->getKind() == SketchPlugin_Point::ID())
+ aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
+ }
+ }
+ if (aCoincPoint[0] && aCoincPoint[1]) {
+ if ((aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end() &&
+ aPoints.find(aCoincPoint[1]) != aPoints.end()) ||
+ (aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() &&
+ aPoints.find(aCoincPoint[0]) != aPoints.end()))
+ aDoF += 2; // point already fixed
+ }
+ }
+ // store fixed points
+ aFixedPoints.insert(aPoints.begin(), aPoints.end());
+ }
+ else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
+ aFeature->getKind() == SketchPlugin_MultiRotation::ID() ||
+ aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) {
+ int aNbCopies = 1;
+ std::string anAttrName;
+ if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID())
+ anAttrName = SketchPlugin_Constraint::ENTITY_B();
+ else {
+ if (aFeature->getKind() == SketchPlugin_MultiRotation::ID())
+ aNbCopies =
+ aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1;
+ else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID())
+ aNbCopies =
+ aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1;
+ anAttrName = SketchPlugin_Constraint::ENTITY_A();
+ }
+
+ AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
+ aFeature->attribute(anAttrName));
+ std::list<ObjectPtr> anObjList = aRefListOfShapes->list();
+ std::list<ObjectPtr>::const_iterator anObjIt = anObjList.begin();
+ for (; anObjIt != anObjList.end(); ++anObjIt) {
+ FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
+ aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
+ }
+ // collect points and their copies for correct calculation of DoF for coincident points
+ collectPointsAndCopies(aFeature, aPointsInMultiConstraints);
+ }
+ }
+
+ aSketchDoF[aSketch] = aDoF;
+ }
+
+ // Check the degrees of freedom are changed
+ std::map<CompositeFeaturePtr, int>::const_iterator aDoFIt = aSketchDoF.begin();
+ std::map<CompositeFeaturePtr, int>::iterator aFound;
+ for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) {
+ if (aDoFIt->second < 0)
+ continue; // conflicting constraints on the current sketch
+ aFound = myDoF.find(aDoFIt->first);
+ if (aFound != myDoF.end() && aFound->second == aDoFIt->second)
+ continue; // nothing is changed
+ myDoF[aDoFIt->first] = aDoFIt->second;
+ // change attribute value
+ std::ostringstream aStream;
+ if (aDoFIt->second == 0)
+ aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")";
+ else
+ aStream << "DOF (degree of freedom) = " << aDoFIt->second;
+ aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str());
+ }
+}
+
+bool SketchSolver_Manager::stopSendUpdate() const
+{