Get CGPath total length

2 Solutions Collect From Internet About “Get CGPath total length”

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.

Recently I had the same issue, but in this case I needed the complete path length regardless the elements involved. e.g lines, curves, arcs, and the solution above doesn’t worked for me, I came up with a general solution.

Here the complete code written in swift UIBezierPath.length

This is the extension that contain the length property.

extension UIBezierPath{
    var length: CGFloat{
        var pathLength:CGFloat = 0.0
        var current = CGPointZero
        var first   = CGPointZero

        self.CGPath.forEach{ element in            
           pathLength += element.distance(to: current, startPoint: first)            

           if element.type == .MoveToPoint{
              first = element.point
           }            
           if element.type != .CloseSubpath{
              current = element.point
           }
       }
      return pathLength
    }
}


// e.g
    let path = UIBezierPath()
    path.lineWidth = 10
    path.moveToPoint(CGPoint(x: 10, y: 10))

    path.addArcWithCenter(
        CGPoint(x: 100,y: 50),
        radius: 50,
        startAngle: CGFloat(140.0.degrees),
        endAngle: CGFloat(40.0.degrees),
        clockwise: false
    )

    path.addLineToPoint(CGPoint(x: 150, y: 30))
    path.closePath()
    print( path.length )