- 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());
- }