+static std::shared_ptr<GeomAPI_Pnt2d> pointCoordinates(const AttributePtr& thePoint)
+{
+ AttributePoint2DPtr aPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(thePoint);
+ return aPnt ? aPnt->pnt() : std::shared_ptr<GeomAPI_Pnt2d>();
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> middlePointOnLine(const FeaturePtr& theFeature)
+{
+ AttributePoint2DPtr aStartAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Line::START_ID()));
+ AttributePoint2DPtr aEndAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Line::END_ID()));
+
+ if (!aStartAttr || !aEndAttr)
+ return std::shared_ptr<GeomAPI_Pnt2d>();
+
+ std::shared_ptr<GeomAPI_XY> aStartPoint = aStartAttr->pnt()->xy();
+ std::shared_ptr<GeomAPI_XY> aEndPoint = aEndAttr->pnt()->xy();
+ return std::shared_ptr<GeomAPI_Pnt2d>(
+ new GeomAPI_Pnt2d(aStartPoint->added(aEndPoint)->multiplied(0.5)));
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> pointOnCircle(const FeaturePtr& theFeature)
+{
+ AttributePoint2DPtr aCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Circle::CENTER_ID()));
+ AttributeDoublePtr aRadius = theFeature->real(SketchPlugin_Circle::RADIUS_ID());
+
+ if (!aCenter || !aRadius)
+ return std::shared_ptr<GeomAPI_Pnt2d>();
+
+ return std::shared_ptr<GeomAPI_Pnt2d>(
+ new GeomAPI_Pnt2d(aCenter->x() + aRadius->value(), aCenter->y()));
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> middlePointOnArc(const FeaturePtr& theFeature)
+{
+ static const double PI = 3.141592653589793238463;
+
+ AttributePoint2DPtr aCenterAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Arc::CENTER_ID()));
+ AttributePoint2DPtr aStartAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Arc::START_ID()));
+ AttributePoint2DPtr aEndAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(SketchPlugin_Arc::END_ID()));
+
+ if (!aCenterAttr || !aStartAttr || !aEndAttr)
+ return std::shared_ptr<GeomAPI_Pnt2d>();
+
+ std::shared_ptr<GeomAPI_Dir2d> aStartDir(new GeomAPI_Dir2d(
+ aStartAttr->x() - aCenterAttr->x(), aStartAttr->y() - aCenterAttr->y()));
+ std::shared_ptr<GeomAPI_Dir2d> aEndDir(new GeomAPI_Dir2d(
+ aEndAttr->x() - aCenterAttr->x(), aEndAttr->y() - aCenterAttr->y()));
+
+ double anAngle = aStartDir->angle(aEndDir);
+ bool isReversed = theFeature->boolean(SketchPlugin_Arc::REVERSED_ID())->value();
+ if (isReversed && anAngle > 0.)
+ anAngle -= 2.0 * PI;
+ else if (!isReversed && anAngle <= 0.)
+ anAngle += 2.0 * PI;
+
+ double cosA = cos(anAngle);
+ double sinA = sin(anAngle);
+
+ // rotate start dir to find middle point on arc
+ double aRadius = aStartAttr->pnt()->distance(aCenterAttr->pnt());
+ double x = aCenterAttr->x() + aRadius * (aStartDir->x() * cosA - aStartDir->y() * sinA);
+ double y = aCenterAttr->y() + aRadius * (aStartDir->x() * sinA + aStartDir->y() * cosA);
+
+ return std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(x, y));
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> pointOnEllipse(const FeaturePtr& theFeature,
+ bool isEllipse = true)
+{
+ const std::string& anAttrName = isEllipse ? SketchPlugin_Ellipse::MAJOR_AXIS_END_ID() :
+ SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID();
+ AttributePoint2DPtr aMajorAxisEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ theFeature->attribute(anAttrName));
+ return aMajorAxisEnd ? aMajorAxisEnd->pnt() : std::shared_ptr<GeomAPI_Pnt2d>();
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> middlePointOnBSpline(const FeaturePtr& theFeature,
+ SketchAPI_Sketch* theSketch)
+{
+ GeomAPI_Edge anEdge(theFeature->lastResult()->shape());
+ GeomPointPtr aMiddle = anEdge.middlePoint();
+ return theSketch->to2D(aMiddle);
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject,
+ SketchAPI_Sketch* theSketch)
+{
+ std::shared_ptr<GeomAPI_Pnt2d> aMiddlePoint;
+ FeaturePtr aFeature = ModelAPI_Feature::feature(theObject);
+ if (aFeature) {
+ // move only features of the following types
+ const std::string& aFeatureKind = aFeature->getKind();
+ if (aFeatureKind == SketchPlugin_Point::ID())
+ aMiddlePoint = pointCoordinates(aFeature->attribute(SketchPlugin_Point::COORD_ID()));
+ else if (aFeatureKind == SketchPlugin_Line::ID())
+ aMiddlePoint = middlePointOnLine(aFeature);
+ else if (aFeatureKind == SketchPlugin_Circle::ID())
+ aMiddlePoint = pointOnCircle(aFeature);
+ else if (aFeatureKind == SketchPlugin_Arc::ID())
+ aMiddlePoint = middlePointOnArc(aFeature);
+ else if (aFeatureKind == SketchPlugin_Ellipse::ID())
+ aMiddlePoint = pointOnEllipse(aFeature);
+ else if (aFeatureKind == SketchPlugin_EllipticArc::ID())
+ aMiddlePoint = pointOnEllipse(aFeature, false);
+ else if (aFeatureKind == SketchPlugin_BSpline::ID() ||
+ aFeatureKind == SketchPlugin_BSplinePeriodic::ID())
+ aMiddlePoint = middlePointOnBSpline(aFeature, theSketch);
+ }
+ return aMiddlePoint;
+}
+
+void SketchAPI_Sketch::move(const ModelHighAPI_RefAttr& theMovedEntity,
+ const std::shared_ptr<GeomAPI_Pnt2d>& theTargetPoint)
+{
+ std::shared_ptr<ModelAPI_ObjectMovedMessage> aMessage(new ModelAPI_ObjectMovedMessage);
+ theMovedEntity.fillMessage(aMessage);
+
+ std::shared_ptr<GeomAPI_Pnt2d> anOriginalPosition;
+ if (aMessage->movedAttribute())
+ anOriginalPosition = pointCoordinates(aMessage->movedAttribute());
+ else
+ anOriginalPosition = middlePoint(aMessage->movedObject(), this);
+
+ if (!anOriginalPosition)
+ return; // something has gone wrong, do not process movement
+
+ aMessage->setOriginalPosition(anOriginalPosition);
+ aMessage->setCurrentPosition(theTargetPoint);
+ Events_Loop::loop()->send(aMessage);
+}
+
+void SketchAPI_Sketch::move(const ModelHighAPI_RefAttr& theMovedEntity,
+ double theTargetX, double theTargetY)
+{
+ std::shared_ptr<GeomAPI_Pnt2d> aTargetPoint(new GeomAPI_Pnt2d(theTargetX, theTargetY));
+ move(theMovedEntity, aTargetPoint);
+}
+
+//--------------------------------------------------------------------------------------
+
+std::shared_ptr<GeomAPI_Pnt2d> SketchAPI_Sketch::to2D(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
+{
+ FeaturePtr aBase = feature();
+ std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
+ aBase->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+ std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+ aBase->attribute(SketchPlugin_Sketch::NORM_ID()));
+ std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+ aBase->attribute(SketchPlugin_Sketch::DIRX_ID()));
+ std::shared_ptr<GeomAPI_Dir> aY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
+
+ return thePoint->to2D(aC->pnt(), aX->dir(), aY);
+}
+
+//--------------------------------------------------------------------------------------
+
+static bool isDifferent(GeomFacePtr theFace1, GeomFacePtr theFace2)
+{
+ // collect edges of the first face
+ std::list<GeomShapePtr> anEdges1;
+ for (GeomAPI_ShapeExplorer anExp(theFace1, GeomAPI_Shape::EDGE); anExp.more(); anExp.next())
+ anEdges1.push_back(anExp.current());
+ // compare edges of faces
+ for (GeomAPI_ShapeExplorer anExp(theFace2, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
+ GeomShapePtr aCurrent = anExp.current();
+ bool isFound = false;
+ std::list<GeomShapePtr>::iterator anIt1 = anEdges1.begin();
+ for (; anIt1 != anEdges1.end(); ++anIt1)
+ if (aCurrent->isSameGeometry(*anIt1)) {
+ isFound = true;
+ anEdges1.erase(anIt1);
+ break;
+ }
+ if (!isFound)
+ return true;
+ }
+ return !anEdges1.empty();
+}
+
+static bool isCustomFacesOrder(CompositeFeaturePtr theSketch)
+{
+ ResultConstructionPtr aSketchResult =
+ std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theSketch->lastResult());
+ if (!aSketchResult)
+ return false;
+
+ std::shared_ptr<GeomAPI_PlanarEdges> aWires =
+ std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aSketchResult->shape());
+ if (!aWires)
+ return false;
+
+ // collect faces constructed by SketchBuilder algorithm
+ GeomAlgoAPI_SketchBuilder aSketchBuilder(aWires->origin(), aWires->dirX(),
+ aWires->norm(), aWires);
+ const ListOfShape& aFaces = aSketchBuilder.faces();
+
+ // compare faces stored in sketch with faces generated by SketchBuilder
+ int aNbSketchFaces = aSketchResult->facesNum();
+ int aFaceIndex = 0;
+ for (ListOfShape::const_iterator aFIt = aFaces.begin();
+ aFIt != aFaces.end() && aFaceIndex < aNbSketchFaces;
+ ++aFIt, ++aFaceIndex) {
+ GeomFacePtr aSketchFace = aSketchResult->face(aFaceIndex);
+ GeomFacePtr aCurFace = (*aFIt)->face();
+ if (isDifferent(aSketchFace, aCurFace))
+ return true;
+ }
+ return false;
+}
+
+static void edgesOfSketchFaces(CompositeFeaturePtr theSketch,
+ std::list<std::list<ResultPtr> >& theEdges)
+{
+ ResultConstructionPtr aSketchResult =
+ std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theSketch->lastResult());
+ if (!aSketchResult)
+ return;
+
+ // collect curves of the sketch
+ std::map<GeomCurvePtr, ResultPtr, GeomAPI_Curve::Comparator> aCurves;
+ int aSubNum = theSketch->numberOfSubs();
+ for (int a = 0; a < aSubNum; ++a) {
+ FeaturePtr aSub = theSketch->subFeature(a);
+ const std::list<ResultPtr>& aResults = aSub->results();
+ std::list<ResultPtr>::const_iterator aRes = aResults.cbegin();
+ for (; aRes != aResults.cend(); aRes++) {
+ GeomShapePtr aCurShape = (*aRes)->shape();
+ if (aCurShape && aCurShape->isEdge())
+ aCurves[untrimmedCurve(aCurShape)] = *aRes;
+ }
+ }
+
+ // convert each face to the list of results of its edges
+ int aFacesNum = aSketchResult->facesNum();
+ for (int a = 0; a < aFacesNum; ++a) {
+ theEdges.push_back(std::list<ResultPtr>());
+ std::list<ResultPtr>& aCurEdges = theEdges.back();
+
+ GeomFacePtr aFace = aSketchResult->face(a);
+ for (GeomAPI_ShapeExplorer anExp(aFace, GeomAPI_Shape::EDGE);
+ anExp.more(); anExp.next()) {
+ GeomCurvePtr aCurrent = untrimmedCurve(anExp.current());
+ aCurEdges.push_back(aCurves[aCurrent]);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------
+