【问题标题】:Adding a QuadCurve to UIBezierPath将 QuadCurve 添加到 UIBezierPath
【发布时间】:2014-01-15 17:00:01
【问题描述】:

我正在做一个绘图应用程序,我正在尝试平滑我用手指绘制的线条。为此,我正在使用“quadCurveToPoint”函数。但是,我没有做对。

下面是我的代码:

- (void) drawRect:(CGRect)rect
{   
    [path stroke];
}

- (id) initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
        self.multipleTouchEnabled = NO;

    path = [UIBezierPath bezierPath];
    path.lineWidth = IS_IPAD? 2.0f : 1.0f;

    return self;
}



- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{   
    UITouch *touch = [touches anyObject];

    m_previousPoint1 = [touch locationInView:self];
    m_previousPoint2 = [touch locationInView:self];
    m_currentPoint  = [touch locationInView:self];
}

//Find the midpoint
CGPoint midPoint(CGPoint p1, CGPoint p2)
{
    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
}


- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{
    UITouch *touch = [touches anyObject];

    m_previousPoint2 = m_previousPoint1;
    m_previousPoint1 = m_currentPoint;

    m_currentPoint = [touch locationInView:self];

    CGPoint mid1 = midPoint(m_previousPoint1, m_previousPoint2);
    CGPoint mid2 = midPoint(m_currentPoint, m_previousPoint1);



    [path setFlatness:1.0f];
    [path setLineCapStyle:kCGLineCapRound];
    [path setLineJoinStyle:kCGLineJoinRound];
    [path moveToPoint:m_previousPoint1];
    [path addLineToPoint:mid1];

    [path addQuadCurveToPoint:mid2 controlPoint:m_currentPoint];
    [self setNeedsDisplay];
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{

}

当我用手指绘图时,我得到如下路径:

我不明白我错过了什么。所以我需要这方面的帮助。

编辑:图片发布 w.r.t.到下面的答案。

谢谢 兰吉特

【问题讨论】:

    标签: ios objective-c uikit core-graphics uibezierpath


    【解决方案1】:

    您的代码看起来不错。但是要解决您提到的问题,请将当前代码更改为以下内容:

    变化:

    [path moveToPoint:m_previousPoint1];
    [path addLineToPoint:mid1];
    

    收件人:

    [path moveToPoint:mid1];
    [path addLineToPoint:m_previousPoint1];
    

    然后改变这个:

    [path addQuadCurveToPoint:mid2 controlPoint:m_currentPoint];
    

    收件人:

    [path addQuadCurveToPoint:m_currentPoint controlPoint:mid2];
    

    经过测试。


    附录(WWDC 算法):

    背景

    根据 WWDC 的想法是这样的:

    1.) 我们不使用当前点,而是使用中点作为起点和终点。

    2.) 因此,我们使用实际的触摸点作为控制点。

    分析/更正您的代码

    所以这是我利用 WWDC 中引入的想法制作的代码的简化版本。

    你明白了。几乎。鉴于上述情况,我们需要将touchesMoved 中的代码更改为以下内容:

    1.)

    如果我们使用中点作为ToPoint 值,我们需要注意第一种情况 当只有一个电流点时,因为 只有一个当前点,我们无法从中得出中点—— 我们需要 2 分

    因此,我们最初需要“读取”当前点之后的一个点来计算中点。以下是这样做的:

    UITouch *touch = [touches anyObject];
    
    m_previousPoint1 = m_currentPoint;
    m_currentPoint = [touch locationInView:self];
    mid1 = midPoint(m_currentPoint, m_previousPoint1);
    
    if(counter == 1)
    {
        [path moveToPoint:m_currentPoint];
        [path addLineToPoint:mid1];
        [self setNeedsDisplay];
    }
    

    变量 counter 最初设置为 0。因此,直到第二个才绘制任何内容 当 counter 为 1 时通过。当它为 1 时,我们将有 2 个点来计算中点。

    接下来就是剩下的部分了:

    2.)

    处理完第一种情况后,我们继续前进到曲线的其余部分并推导出 适当地与我们连接的点 细分:

    else if(counter > 1)
    {
        [path addQuadCurveToPoint:mid1 controlPoint:m_previousPoint1];
        [self setNeedsDisplay];
    }
    counter++;
    

    这是上面第一个if 之后的else if。当只处理第一种情况时,我们进入这里,因为我使用一个简单的计数器并在每次调用 touchesMoved 时递增它。

    这里发生的是,我们使用前一个点作为控制点从前一个中点连接到mid1。那么,当前的点呢?我们一直在使用它,直到下一次通过。

    3.) 最后,我们关注touchesEnded 中曲线的最后一段:

    - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [path addLineToPoint:[[touches anyObject] locationInView:self]];
        [self setNeedsDisplay];
    }
    

    这只是从你的中点到最后一点画一条线。

    最后在touchesBegan中,我设置了counter = 0;,所以下一条曲线会再次开始上述过程。

    我使用模拟器和设备测试了上述内容,这是一个屏幕截图:

    这里是完整的源代码:

    - (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
    {
        UITouch *touch = [touches anyObject];
        counter = 0;
        m_previousPoint1 = [touch locationInView:self];
        m_currentPoint  = [touch locationInView:self];
    }
    
    //Find the midpoint
    CGPoint midPoint(CGPoint p1, CGPoint p2)
    {
        return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
    }
    
    
    - (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
    {
        UITouch *touch = [touches anyObject];
    
        m_previousPoint1 = m_currentPoint;
        m_currentPoint = [touch locationInView:self];
    
        mid1 = midPoint(m_currentPoint, m_previousPoint1);
    
        [path setFlatness:1.0f];
        [path setLineCapStyle:kCGLineCapRound];
        [path setLineJoinStyle:kCGLineJoinRound];
    
        if(counter == 1)
        {
            [path moveToPoint:m_currentPoint];
            [path addLineToPoint:mid1];
            [self setNeedsDisplay];
        }
        else if(counter > 1)
        {
            [path addQuadCurveToPoint:mid1 controlPoint:m_previousPoint1];
            [self setNeedsDisplay];
        }
        counter++;
    }
    
    - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [path addLineToPoint:[[touches anyObject] locationInView:self]];
        [self setNeedsDisplay];
    }
    

    【讨论】:

    • 嘿,谢谢你的回答,我正在旅行,所以现在无法检查,但我回来后会尽快检查。我想知道您是如何解决这个问题的,无论是通过反复试验还是对概念的正确理解?如果是对概念的正确理解,有什么秘诀,你想给我,让我能更好的理解概念。谢谢
    • 你好,谢谢,我试过你的代码,它可以工作,但我没有得到平滑的曲线。你能就此提出一些建议吗?
    • 检查上面的图片我是如何得到曲线的。
    • 嘿,还请查看 WWDC 2012 视频会话 233 - 从 38:31 开始构建高级手势识别器,他们讨论了曲线平滑,这里我不明白他们如何创建“LocationSamples”数组跨度>
    • 你好@Unheilig,你经历过吗。
    猜你喜欢
    • 1970-01-01
    • 2015-12-19
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多