+std::shared_ptr<ModelAPI_Feature> SketchPlugin_Sketch::addFeature(std::string theID)
+{
+ // Set last feature of the sketch as current feature.
+ // Reason: Changing of parameter through Python API may lead to creation of new features
+ // (e.g. changing number of copies in MultiRotation). If the sketch is not the last
+ // feature in the Object Browser, then new features will be added to the end feature.
+ // Therefore, setting any feature below the sketch as a current feature will disable
+ // these newly created features.
+ std::shared_ptr<ModelAPI_AttributeRefList> aRefList = std::dynamic_pointer_cast<
+ ModelAPI_AttributeRefList>(data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
+ int aSize = aRefList->size(false);
+ ObjectPtr aLastObject = aSize == 0 ? data()->owner() : aRefList->object(aSize - 1, false);
+ FeaturePtr aLastFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastObject);
+ document()->setCurrentFeature(aLastFeature, false);
+
+ // add new feature
+ std::shared_ptr<ModelAPI_Feature> aNew = document()->addFeature(theID, false);
+ if (aNew) {
+ // the sketch cannot be specified for the macro-features defined in python
+ // like SketchRectangle, so we need to check the type of new feature
+ std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
+ std::dynamic_pointer_cast<SketchPlugin_Feature>(aNew);
+ if (aSketchFeature)
+ aSketchFeature->setSketch(this);
+ data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->append(aNew);
+ }
+ // set as current also after it becomes sub to set correctly enabled for other sketch subs
+ document()->setCurrentFeature(aNew, false);
+
+ return aNew;
+}
+
+void SketchPlugin_Sketch::removeFeature(std::shared_ptr<ModelAPI_Feature> theFeature)
+{
+ if (!data()->isValid()) // sketch is already removed (case on undo of sketch), sync is not needed
+ return;
+ AttributeRefListPtr aList = reflist(SketchPlugin_Sketch::FEATURES_ID());
+ // if the object is last, remove it from the list
+ // (needed to skip empty transaction on edit of sketch feature)
+ if (aList->object(aList->size(true) - 1, true) == theFeature) {
+ aList->remove(theFeature);
+ } else {
+ // to keep the persistent sub-elements indexing, do not remove elements from list,
+ // but substitute by nulls
+ aList->substitute(theFeature, ObjectPtr());
+ }
+}
+
+int SketchPlugin_Sketch::numberOfSubs(bool forTree) const
+{
+ if (forTree)
+ return 0;
+ return data()->reflist(FEATURES_ID())->size(false);
+}
+
+std::shared_ptr<ModelAPI_Feature> SketchPlugin_Sketch::subFeature(
+ const int theIndex, bool forTree)
+{
+ if (forTree)
+ return FeaturePtr();
+
+ ObjectPtr anObj = data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->object(theIndex, false);
+ FeaturePtr aRes = std::dynamic_pointer_cast<ModelAPI_Feature>(anObj);
+ return aRes;
+}
+
+int SketchPlugin_Sketch::subFeatureId(const int theIndex) const
+{
+ std::shared_ptr<ModelAPI_AttributeRefList> aRefList = std::dynamic_pointer_cast<
+ ModelAPI_AttributeRefList>(data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
+ std::list<ObjectPtr> aFeatures = aRefList->list();
+ std::list<ObjectPtr>::const_iterator anIt = aFeatures.begin();
+ int aResultIndex = 1; // number of the counted (created) features, started from 1
+ int aFeatureIndex = -1; // number of the not-empty features in the list
+ for (; anIt != aFeatures.end(); anIt++) {
+ if (anIt->get())
+ aFeatureIndex++;
+ if (aFeatureIndex == theIndex)
+ break;
+ aResultIndex++;
+ }
+ return aResultIndex;
+}
+
+bool SketchPlugin_Sketch::isSub(ObjectPtr theObject) const
+{
+ // check is this feature of result
+ FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
+ if (!aFeature) {
+ ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
+ if (aRes)
+ aFeature = document()->feature(aRes);
+ }
+ if (aFeature) {
+ return data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->isInList(aFeature);
+ }
+ return false;
+}
+
+
+static bool isOrigin(const GeomPointPtr& thePoint, const double theTolerance)
+{
+ return fabs(thePoint->x()) < theTolerance &&
+ fabs(thePoint->y()) < theTolerance &&
+ fabs(thePoint->z()) < theTolerance;
+}
+
+static bool isCoordinateAxis(const GeomDirPtr& theDir, const double theTolerance)
+{
+ return fabs(theDir->x() - 1.0) < theTolerance || fabs(theDir->x() + 1.0) < theTolerance ||
+ fabs(theDir->y() - 1.0) < theTolerance || fabs(theDir->y() + 1.0) < theTolerance ||
+ fabs(theDir->z() - 1.0) < theTolerance || fabs(theDir->z() + 1.0) < theTolerance;
+}
+
+static bool isCoordinatePlane(const GeomAx3Ptr& thePlane)
+{
+ static const double THE_TOLERANCE = 1.e-7;
+ if (!thePlane)
+ return false;
+
+ GeomPointPtr anOrigin = thePlane->origin();
+ GeomDirPtr aNormal = thePlane->normal();
+ GeomDirPtr aDirX = thePlane->dirX();
+
+ return isOrigin(anOrigin, THE_TOLERANCE) &&
+ isCoordinateAxis(aNormal, THE_TOLERANCE) &&
+ isCoordinateAxis(aDirX, THE_TOLERANCE);
+}
+
+void SketchPlugin_Sketch::attributeChanged(const std::string& theID) {
+ if (theID == SketchPlugin_SketchEntity::EXTERNAL_ID()) {
+ AttributeSelectionPtr aSelAttr = selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
+ if (aSelAttr->context().get()) { // update arguments due to the selection value
+ std::shared_ptr<GeomAPI_Shape> aSelection = aSelAttr->value();
+ if (!aSelection.get()) aSelection = aSelAttr->context()->shape();
+ // update the sketch plane
+ std::shared_ptr<GeomAPI_Face> aFace;
+ if (aSelection->isFace()) {
+ aFace = aSelection->face();
+ } else if (aSelection->isCompound()) {
+ GeomAPI_ShapeIterator anIt(aSelection);
+ aFace = anIt.current()->face();
+ }
+ if (aFace.get()) {
+ std::shared_ptr<GeomAPI_Pln> aPlane = aFace->getPlane();
+ if (aPlane.get()) {
+ double anA, aB, aC, aD;
+ aPlane->coefficients(anA, aB, aC, aD);
+
+ // calculate attributes of the sketch
+ std::shared_ptr<GeomAPI_Dir> aNormDir(new GeomAPI_Dir(anA, aB, aC));
+ std::shared_ptr<GeomAPI_XYZ> aCoords = aNormDir->xyz();
+ std::shared_ptr<GeomAPI_XYZ> aZero(new GeomAPI_XYZ(0, 0, 0));
+ aCoords = aCoords->multiplied(-aD * aCoords->distance(aZero));
+ std::shared_ptr<GeomAPI_Pnt> anOrigPnt(new GeomAPI_Pnt(aCoords));
+ // X axis is preferable to be dirX on the sketch
+ // here can not be very small value to avoid very close to X normal axis (issue 595)
+ static const double tol = 0.1;
+ bool isX = fabs(anA) - 1.0 < tol && fabs(aB) < tol && fabs(aC) < tol;
+ std::shared_ptr<GeomAPI_Dir> aTempDir(
+ isX ? new GeomAPI_Dir(0, 1, 0) : new GeomAPI_Dir(1, 0, 0));
+ std::shared_ptr<GeomAPI_Dir> aYDir(new GeomAPI_Dir(aNormDir->cross(aTempDir)));
+ std::shared_ptr<GeomAPI_Dir> aXDir(new GeomAPI_Dir(aYDir->cross(aNormDir)));
+
+ bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+ // update position of the sketch
+ std::shared_ptr<GeomDataAPI_Point> anOrigin = std::dynamic_pointer_cast
+ <GeomDataAPI_Point>(data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+ anOrigin->setValue(anOrigPnt);
+ std::shared_ptr<GeomDataAPI_Dir> aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+ data()->attribute(SketchPlugin_Sketch::NORM_ID()));
+ aNormal->setValue(aNormDir);
+ std::shared_ptr<GeomDataAPI_Dir> aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+ data()->attribute(SketchPlugin_Sketch::DIRX_ID()));
+ aDirX->setValue(aXDir);
+ data()->blockSendAttributeUpdated(aWasBlocked, true);
+ }
+ }
+ }
+ } else if (theID == NORM_ID() || theID == DIRX_ID() || theID == ORIGIN_ID()) {
+ // check if current and previous sketch planes are coordinate planes and they are different
+ GeomAx3Ptr aCurPlane;
+ bool areCoordPlanes = false;
+ if (isPlaneSet()) {
+ aCurPlane = coordinatePlane();
+ areCoordPlanes = isCoordinatePlane(aCurPlane) && isCoordinatePlane(myPlane);
+ }
+
+ // send all sub-elements are also updated: all entities become created on different plane
+ static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
+ std::list<ObjectPtr> aSubs = data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->list();
+ std::list<ObjectPtr>::iterator aSub = aSubs.begin();
+ for(; aSub != aSubs.end(); aSub++) {
+ if (aSub->get()) {
+ if (areCoordPlanes)
+ updateCoordinateAxis(*aSub, aCurPlane);
+
+ ModelAPI_EventCreator::get()->sendUpdated(*aSub, anUpdateEvent);
+ }
+ }
+ if (aCurPlane)
+ myPlane = aCurPlane;
+ }
+}
+
+void SketchPlugin_Sketch::createPoint2DResult(ModelAPI_Feature* theFeature,
+ SketchPlugin_Sketch* theSketch,
+ const std::string& theAttributeID, const int theIndex)