Xamarin iOS CGPath 绘制具有 curved/rounded 个角的多边形

Xamarin iOS CGPath draw polygon with curved/rounded corners

我正在尝试创建一个 class 在自定义 UIView 子 class 中绘制多边形。我有它的工作,但现在我想通过四舍五入来平滑角落,我不知道该怎么做。这是我目前所拥有的:

public class MyView : UIView
{
    private List<CGPoint> points;

    public MyView(CGRect frame, List<CGPoint> points) : base (frame)
    {
        this.points = points;
    }

    public override Draw (CGRect rect)
    {
        CGContext context = UIGraphics.GetCurrentContext();

        context.SetLineWidth(2);
        UIColor.Black.SetStroke();
        UIColor.Green.SetFill();

        CGPath path = new CGPath();
        path.AddLines(points.ToArray());
        path.CloseSubpath();

        context.AddPath(path);
        context.Clip();

        using (CGColorSpace rgb = CGColorSpace.CreateDeviceRGB())
        {
            CGGradient gradient = new CGGradient (rgb, new CGColor[]
            {
                new CGColor(0, 1, 0),
                new CGColor(0, 0.5f, 0),
                new CGColor(0, 1, 0),
                new CGColor(0, 0.5f, 0)
            });

            context.DrawLinearGradient(gradient,
                new CGPoint (path.BoundingBox.Left, path.BoundingBox.Top), 
                new CGPoint (path.BoundingBox.Right, path.BoundingBox.Bottom), 
                CGGradientDrawingOptions.DrawsBeforeStartLocation);
        }
    }
}

有了我投入的分数,我得到了这个:

我试过使用 path.AddCurveToPointpath.AddArc,但我似乎无法让它们按我想要的方式工作。任何帮助将不胜感激。

编辑

我玩过 path.AddCurveToPoint 现在它看起来像这样:

它一开始是正确的,但后来就完全疯了。不确定我在这里做错了什么。这是我更新的 Draw 覆盖:

public override Draw (CGRect rect)
{
    CGContext context = UIGraphics.GetCurrentContext();

    context.SetLineWidth(2);
    UIColor.Black.SetStroke();
    UIColor.Green.SetFill();

    CGPath path = new CGPath();
    path.AddLines(points.ToArray());
    path.CloseSubpath();
    // updated section
    int count = points.Count;

    for (int x = 1; x < count; x++)
    {
        var p = points[x];
        if (x != 0)
        {
            var prev = points[x - 1];
            if (prev.Y != p.Y)
            {
                if (prev.Y > p.Y)
                {
                    path.AddCurveToPoint(prev, new CGPoint(p.X - prev.X, p.Y), p);
                }
                else
                {
                    //???
                }
            }
        }
    }
    // end updated section
    context.AddPath(path);
    context.Clip();

    using (CGColorSpace rgb = CGColorSpace.CreateDeviceRGB())
    {
        CGGradient gradient = new CGGradient (rgb, new CGColor[]
        {
            new CGColor(0, 1, 0),
            new CGColor(0, 0.5f, 0),
            new CGColor(0, 1, 0),
            new CGColor(0, 0.5f, 0)
        });

        context.DrawLinearGradient(gradient,
            new CGPoint (path.BoundingBox.Left, path.BoundingBox.Top), 
            new CGPoint (path.BoundingBox.Right, path.BoundingBox.Bottom), 
            CGGradientDrawingOptions.DrawsBeforeStartLocation);
    }
}

更新:

通过您的更新,我现在看到了您的问题。您正在尝试将您的点列表用作贝塞尔控制点和多边形角的绘制点,但这行不通。

"easiest" 使用现有点列表作为控制点并在每个控制点之间创建中间结并向每个控制点添加四边形曲线的方法分割。

注意:更难的方法是使用点列表作为节点,并为每个贝塞尔曲线段创建控制点。我不会在这里深入探讨,但许多图表系统使用此 article 作为参考,getControlPoints 中的 return 值将用作方法 AddCurveToPoint.

简单方法示例:

var color = UIColor.Red;
var color2 = UIColor.White;

var points = new List<CGPoint>() { 
    new CGPoint(136.49f, 134.6f),
    new CGPoint(197.04f, 20.0f),
    new CGPoint(257.59f, 20.0f),
    new CGPoint(303.0f, 134.6f),
    new CGPoint(303.0f, 252.0f),
    new CGPoint(28.0f, 252.0f),
    new CGPoint(28.0f, 134.6f),
    new CGPoint(136.49f, 134.6f)
};

UIBezierPath polygonPath = new UIBezierPath();
polygonPath.MoveTo(points[0]);
polygonPath.AddLineTo(points[1]);
polygonPath.AddLineTo(points[2]);
polygonPath.AddLineTo(points[3]);
polygonPath.AddLineTo(points[4]);
polygonPath.AddLineTo(points[5]);
polygonPath.AddLineTo(points[6]);
polygonPath.ClosePath();
polygonPath.Fill();
color2.SetStroke();
polygonPath.LineWidth = 10.0f;
polygonPath.Stroke();

UIBezierPath smoothedPath = new UIBezierPath();
var m0 = MidPoint(points[0], points[1]);
smoothedPath.MoveTo(m0);
smoothedPath.AddQuadCurveToPoint(m0, points[0]);
var m1 = MidPoint(points[1], points[2]);
smoothedPath.AddQuadCurveToPoint(m1, points[1]);
var m2 = MidPoint(points[2], points[3]);
smoothedPath.AddQuadCurveToPoint(m2, points[2]);
var m3 = MidPoint(points[3], points[4]);
smoothedPath.AddQuadCurveToPoint(m3, points[3]);
var m4 = MidPoint(points[4], points[5]);
smoothedPath.AddQuadCurveToPoint(m4, points[4]);
var m5 = MidPoint(points[5], points[6]);
smoothedPath.AddQuadCurveToPoint(m5, points[5]);
var m6 = MidPoint(points[6], points[0]);
smoothedPath.AddQuadCurveToPoint(m6, points[6]);

smoothedPath.AddQuadCurveToPoint(m0, points[0]); 
smoothedPath.ClosePath();

color.SetStroke();
smoothedPath.LineWidth = 5.0f;
smoothedPath.Stroke();

原文:

1) 我不确定您正在寻找多少 联合平滑,但是 CGLineJoin.Round 对于 UIBezierPath 上的 LineJoinStyle 将创造微妙的外观。

2) 如果需要重角平滑,可以手动计算每条线的start/end,并添加一个ArcWithCenter连接每条线段。

3) 或者如果没有手动角弧计算,您可以在使用 CGLineJoin.Round 时设置非常粗的线宽,并将绘图的尺寸稍微缩小以弥补增加的线宽。

示例:

var color = UIColor.FromRGBA(0.129f, 0.467f, 0.874f, 1.000f);

UIBezierPath polygonPath = new UIBezierPath();
polygonPath.MoveTo(new CGPoint(136.49f, 134.6f));
polygonPath.AddLineTo(new CGPoint(197.04f, 20.0f));
polygonPath.AddLineTo(new CGPoint(257.59f, 20.0f));
polygonPath.AddLineTo(new CGPoint(303.0f, 134.6f));
polygonPath.AddLineTo(new CGPoint(303.0f, 252.0f));
polygonPath.AddLineTo(new CGPoint(28.0f, 252.0f));
polygonPath.AddLineTo(new CGPoint(28.0f, 134.6f));
polygonPath.AddLineTo(new CGPoint(136.49f, 134.6f));
polygonPath.ClosePath();
polygonPath.LineJoinStyle = CGLineJoin.Round;

color.SetFill();
polygonPath.Fill();
color.SetStroke();
polygonPath.LineWidth = 30.0f;
polygonPath.Stroke();

:中点法

protected CGPoint MidPoint(CGPoint first, CGPoint second)
{
    var x = (first.X + second.X) / 2;
    var y = (first.Y + second.Y) / 2;
    return new CGPoint(x: x, y: y);
}