/// <summary> /// Initializes a new instance of the <see cref="EllipseGeometry"/> class. /// </summary> /// <param name="rect">The rectangle that the ellipse should fill.</param> public EllipseGeometry(Rect rect) { IPlatformRenderInterface factory = PerspexLocator.Current.GetService <IPlatformRenderInterface>(); IStreamGeometryImpl impl = factory.CreateStreamGeometry(); using (IStreamGeometryContextImpl ctx = impl.Open()) { double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3; var center = rect.Center; var radius = new Vector(rect.Width / 2, rect.Height / 2); var x0 = center.X - radius.X; var x1 = center.X - (radius.X * controlPointRatio); var x2 = center.X; var x3 = center.X + (radius.X * controlPointRatio); var x4 = center.X + radius.X; var y0 = center.Y - radius.Y; var y1 = center.Y - (radius.Y * controlPointRatio); var y2 = center.Y; var y3 = center.Y + (radius.Y * controlPointRatio); var y4 = center.Y + radius.Y; ctx.BeginFigure(new Point(x2, y0), true); ctx.CubicBezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2)); ctx.CubicBezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4)); ctx.CubicBezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2)); ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0)); ctx.EndFigure(true); } PlatformImpl = impl; }
/// <summary> /// Draws a Bezier curve to the specified point. /// </summary> /// <param name="point1">The first control point used to specify the shape of the curve.</param> /// <param name="point2">The second control point used to specify the shape of the curve.</param> /// <param name="point3">The destination point for the end of the curve.</param> public void CubicBezierTo(Point point1, Point point2, Point point3) { _impl.CubicBezierTo(point1, point2, point3); }
/// <summary> /// Draws a Bezier curve to the specified point. /// </summary> /// <param name="point1">The first control point used to specify the shape of the curve.</param> /// <param name="point2">The second control point used to specify the shape of the curve.</param> /// <param name="point3">The destination point for the end of the curve.</param> public void CubicBezierTo(Point point1, Point point2, Point point3) { _impl.CubicBezierTo(point1, point2, point3); _currentPoint = point3; }
public static void QuadraticBezierTo(IStreamGeometryContextImpl context, Point current, Point controlPoint, Point endPoint) { //(s, (s + 2c)/ 3, (e + 2c)/ 3, e) context.CubicBezierTo((current + 2*controlPoint)/3, (endPoint + 2*controlPoint)/3, endPoint); }
public static void QuadraticBezierTo(IStreamGeometryContextImpl context, Point current, Point controlPoint, Point endPoint) { //(s, (s + 2c)/ 3, (e + 2c)/ 3, e) context.CubicBezierTo((current + 2 * controlPoint) / 3, (endPoint + 2 * controlPoint) / 3, endPoint); }
/// <summary> /// Builds the arc outline using given StreamGeometryContext /// </summary> /// <param name="path">A StreamGeometryContext to output the path commands to</param> /// <param name="degree">degree of the Bezier curve to use</param> /// <param name="threshold">acceptable error</param> /// <param name="openNewFigure">if true, a new figure will be started in the specified StreamGeometryContext</param> public void BuildArc(IStreamGeometryContextImpl path, int degree, double threshold, bool openNewFigure) { if (degree < 1 || degree > _maxDegree) throw new ArgumentException($"degree should be between {1} and {_maxDegree}", nameof(degree)); // find the number of Bezier curves needed bool found = false; int n = 1; double dEta; double etaB; while (!found && n < 1024) { dEta = (Eta2 - Eta1) / n; if (dEta <= 0.5 * Math.PI) { etaB = Eta1; found = true; for (int i = 0; found && i < n; ++i) { double etaA = etaB; etaB += dEta; found = EstimateError(degree, etaA, etaB) <= threshold; } } n = n << 1; } dEta = (Eta2 - Eta1) / n; etaB = Eta1; double cosEtaB = Math.Cos(etaB); double sinEtaB = Math.Sin(etaB); double aCosEtaB = A * cosEtaB; double bSinEtaB = B * sinEtaB; double aSinEtaB = A * sinEtaB; double bCosEtaB = B * cosEtaB; double xB = Cx + aCosEtaB * _cosTheta - bSinEtaB * _sinTheta; double yB = Cy + aCosEtaB * _sinTheta + bSinEtaB * _cosTheta; double xBDot = -aSinEtaB * _cosTheta - bCosEtaB * _sinTheta; double yBDot = -aSinEtaB * _sinTheta + bCosEtaB * _cosTheta; /* This controls the drawing in case of pies if (openNewFigure) { if (IsPieSlice) { path.BeginFigure(new Point(Cx, Cy), false, false); path.LineTo(new Point(xB, yB), true, true); } else { path.BeginFigure(new Point(xB, yB), false, false); } } else { //path.LineTo(new Point(xB, yB), true, true); } */ //otherwise we're supposed to be already at the (xB,yB) double t = Math.Tan(0.5 * dEta); double alpha = Math.Sin(dEta) * (Math.Sqrt(4 + 3 * t * t) - 1) / 3; for (int i = 0; i < n; ++i) { //double etaA = etaB; double xA = xB; double yA = yB; double xADot = xBDot; double yADot = yBDot; etaB += dEta; cosEtaB = Math.Cos(etaB); sinEtaB = Math.Sin(etaB); aCosEtaB = A * cosEtaB; bSinEtaB = B * sinEtaB; aSinEtaB = A * sinEtaB; bCosEtaB = B * cosEtaB; xB = Cx + aCosEtaB * _cosTheta - bSinEtaB * _sinTheta; yB = Cy + aCosEtaB * _sinTheta + bSinEtaB * _cosTheta; xBDot = -aSinEtaB * _cosTheta - bCosEtaB * _sinTheta; yBDot = -aSinEtaB * _sinTheta + bCosEtaB * _cosTheta; if (degree == 1) { path.LineTo(new Point(xB, yB)); } else if (degree == 2) { double k = (yBDot * (xB - xA) - xBDot * (yB - yA)) / (xADot * yBDot - yADot * xBDot); path.QuadraticBezierTo(new Point(xA + k * xADot, yA + k * yADot), new Point(xB, yB)); } else { path.CubicBezierTo( new Point(xA + alpha * xADot, yA + alpha * yADot), new Point(xB - alpha * xBDot, yB - alpha * yBDot), new Point(xB, yB) ); } } if (IsPieSlice) { path.LineTo(new Point(Cx, Cy)); } }