Get CGPath total length

前端 未结 1 1154
执念已碎
执念已碎 2020-12-24 15:18

I have many UIBezierPath that I am animating with CALyerAnimation @\"StrokeStart\" and @\"strokeEnd\".

I wish that the animation will have the same speed for all th

相关标签:
1条回答
  • 2020-12-24 16:03

    While you can calculate Bezier path's length by integrating it numerically, it can be done much easier by dividing path into linear segments. If the segments are small enough the approximation error should be neglectable, especially that you are just trying to animate it.

    I'll show you function for quad curves, but you can easily incorporate the solution for cubic curves as well:

    - (float) bezierCurveLengthFromStartPoint: (CGPoint) start toEndPoint: (CGPoint) end withControlPoint: (CGPoint) control
    {
        const int kSubdivisions = 50;
        const float step = 1.0f/(float)kSubdivisions;
    
        float totalLength = 0.0f;
        CGPoint prevPoint = start;
    
        // starting from i = 1, since for i = 0 calulated point is equal to start point
        for (int i = 1; i <= kSubdivisions; i++)
        {
            float t = i*step;
    
            float x = (1.0 - t)*(1.0 - t)*start.x + 2.0*(1.0 - t)*t*control.x + t*t*end.x;
            float y = (1.0 - t)*(1.0 - t)*start.y + 2.0*(1.0 - t)*t*control.y + t*t*end.y;
    
            CGPoint diff = CGPointMake(x - prevPoint.x, y - prevPoint.y);
    
            totalLength += sqrtf(diff.x*diff.x + diff.y*diff.y); // Pythagorean
    
            prevPoint = CGPointMake(x, y);
        }
    
        return totalLength;
    }
    

    EDIT

    If you don't have access to path control points (say you created path using arcs) you can always access underlying Bezier curves using CGPathApply function:

    - (void) testPath
    {
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointZero];
        [path addQuadCurveToPoint:CGPointMake(1, 1) controlPoint:CGPointMake(-2, 2)];
    
        CGPathRef p = path.CGPath;
    
        CGPathApply(p, nil, pathFunction);
    }
    
    void pathFunction(void *info, const CGPathElement *element)
    {
        if (element->type == kCGPathElementAddQuadCurveToPoint)
        {
            CGPoint p;
            p = element->points[0]; // control point
            NSLog(@"%lg %lg", p.x, p.y);
    
            p = element->points[1]; // end point
            NSLog(@"%lg %lg", p.x, p.y);
        }
        // check other cases as well!
    }
    

    Note that it doesn't provide the path's start point, but it's easy to keep track of it on your own.

    0 讨论(0)
提交回复
热议问题