Adding a QuadCurve to UIBezierPath

ぃ、小莉子 提交于 2020-01-01 17:05:48

问题


I am doing a drawing app and I am trying to smooth the line which I draw with finger.For this purpose I am using "quadCurveToPoint" function. But, I am not getting it right.

Below is my code:

- (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
{

}

When I draw with my finger, I am geting a path like this below:

I am not understanding what I am missing. So I need help in this regard.

EDIT: Image posted w.r.t. to the below answer.

Thanks Ranjit


回答1:


Your code looks good. But to fix the problem you mentioned, change the current code to the following:

Change:

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

To:

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

And Change this:

[path addQuadCurveToPoint:mid2 controlPoint:m_currentPoint];

To:

[path addQuadCurveToPoint:m_currentPoint controlPoint:mid2];

Tested.


Addendum (WWDC Algorithm):

Background:

The idea is this according to WWDC:

1.) Instead of using the current point, we use the mid points as the start point and end point.

2.) As a result, we use the actual touch points as control points.

Analysis / Correction for your code:

So here is a simplified version of code I made making use the idea that was introduced in WWDC.

You got the idea. Almost. Given the above, we need to make changes to your code in touchesMoved to the following:

1.)

If we are using the mid point as the ToPoint value, we need to take care the first case when there is only one current point, because with only one current point, we cannot derive a mid point from it - we need 2 points.

So, we would need to "read" one point past the current point initially to calculate the mid point. The following does that:

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];
}

The variable counter is initially set to 0. So, nothing gets drawn until the second pass when counter is 1. And when it is, we will have 2 points to calculate the mid point.

Then here comes the rest of the touches:

2.)

Once the first case is taken care of, we move forward to the rest of the curve and derive appropriately the points with which we connect the segments:

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

Here is the else if right after the first if above. We enter here when only the first case is handled, for that I use a simple counter and increment it every time touchesMoved gets called.

What happens here is that we are connecting from the previous mid point to mid1 using the previous point as control point. So, what about the current point? We are using it until the next pass.

3.) And finally, we take care the last segment of the curve in touchesEnded:

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

This simply draws a line from your mid point to the last point.

And finally in touchesBegan, I set counter = 0;, so the next curve will start the above process again.

I tested the above using both simulator and device, and here is a screen shot:

And here is the complete source:

- (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];
}


来源:https://stackoverflow.com/questions/21143614/adding-a-quadcurve-to-uibezierpath

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!