Skip to content

Instantly share code, notes, and snippets.

@warpling
Forked from janselv/UIBezierPathLength.swift
Created July 21, 2016 20:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save warpling/c959f2c53c4bc82cda657558b6a63867 to your computer and use it in GitHub Desktop.
Save warpling/c959f2c53c4bc82cda657558b6a63867 to your computer and use it in GitHub Desktop.
Get total iOS CGPath length as an extension of UIBezierPath written in swift
// The MIT License (MIT)
//
// Copyright (c) 2016 Jansel Valentin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension CGPath{
func forEach(callback: (CGPathElement)->() ){
typealias Callback = (CGPathElement) -> ()
func apply(info: UnsafeMutablePointer<Void>, element:UnsafePointer<CGPathElement>){
let callback = UnsafeMutablePointer<Callback>(info)
callback.memory(element.memory)
}
var calle = { callback($0) }
withUnsafeMutablePointer(&calle) { pointer in
CGPathApply(self, pointer, apply)
}
}
}
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
}
}
extension CGPathElement{
var point: CGPoint{
switch type {
case .MoveToPoint, .AddLineToPoint:
return self.points[0]
case .AddQuadCurveToPoint:
return self.points[1]
case .AddCurveToPoint:
return self.points[2]
case .CloseSubpath:
return CGRectNull.origin
}
}
func distance(to point: CGPoint, startPoint: CGPoint ) -> CGFloat{
switch type {
case .MoveToPoint:
return 0.0
case .CloseSubpath:
return point.distance(to:startPoint)
case .AddLineToPoint:
return point.distance(to:self.points[0])
case .AddCurveToPoint:
return BezierCurveLength(point, c1: self.points[0], c2: self.points[1], p1: self.points[2])
case .AddQuadCurveToPoint:
return BezierCurveLength(point, c1: self.points[0], p1: self.points[1])
}
}
}
extension CGPoint{
func distance(to to:CGPoint) -> CGFloat{
let dx = pow(to.x - self.x,2)
let dy = pow(to.y - self.y,2)
return sqrt(dx+dy)
}
}
// Helper Functions
func CubicBezierCurveFactors(t:CGFloat) -> (CGFloat,CGFloat,CGFloat,CGFloat){
let t1 = pow(1.0-t, 3.0)
let t2 = 3.0*pow(1.0-t,2.0)*t
let t3 = 3.0*(1.0-t)*pow(t,2.0)
let t4 = pow(t, 3.0)
return (t1,t2,t3,t4)
}
func QuadBezierCurveFactors(t:CGFloat) -> (CGFloat,CGFloat,CGFloat){
let t1 = pow(1.0-t,2.0)
let t2 = 2.0*(1-t)*t
let t3 = pow(t, 2.0)
return (t1,t2,t3)
}
// Quadratic Bezier Curve
func BezierCurve(t t:CGFloat,p0:CGFloat,c1:CGFloat,p1:CGFloat) -> CGFloat{
let factors = QuadBezierCurveFactors(t)
return (factors.0*p0) + (factors.1*c1) + (factors.2*p1)
}
// Quadratic Bezier Curve
func BezierCurve(t t:CGFloat,p0:CGPoint,c1:CGPoint,p1:CGPoint) -> CGPoint{
let x = BezierCurve(t: t, p0: p0.x, c1: c1.x, p1: p1.x)
let y = BezierCurve(t: t, p0: p0.y, c1: c1.y, p1: p1.y)
return CGPoint(x: x, y: y)
}
// Cubic Bezier Curve
func BezierCurve(t t:CGFloat,p0:CGFloat, c1:CGFloat, c2:CGFloat, p1:CGFloat) -> CGFloat{
let factors = CubicBezierCurveFactors(t)
return (factors.0*p0) + (factors.1*c1) + (factors.2*c2) + (factors.3*p1)
}
// Cubic Bezier Curve
func BezierCurve(t t: CGFloat, p0:CGPoint, c1:CGPoint, c2: CGPoint, p1: CGPoint) -> CGPoint{
let x = BezierCurve(t: t, p0: p0.x, c1: c1.x, c2: c2.x, p1: p1.x)
let y = BezierCurve(t: t, p0: p0.y, c1: c1.y, c2: c2.y, p1: p1.y)
return CGPoint(x: x, y: y)
}
// Cubic Bezier Curve Length
func BezierCurveLength(p0:CGPoint,c1:CGPoint, c2:CGPoint, p1:CGPoint) -> CGFloat{
let steps = 12 // on greater samples, more presicion
var current = p0
var previous = p0
var length:CGFloat = 0.0
for i in 1...steps{
let t = CGFloat(i) / CGFloat(steps)
current = BezierCurve(t: t, p0: p0, c1: c1, c2: c2, p1: p1)
length += previous.distance(to: current)
previous = current
}
return length
}
// Quadratic Bezier Curve Length
func BezierCurveLength(p0:CGPoint,c1:CGPoint, p1:CGPoint) -> CGFloat{
let steps = 12 // on greater samples, more presicion
var current = p0
var previous = p0
var length:CGFloat = 0.0
for i in 1...steps{
let t = CGFloat(i) / CGFloat(steps)
current = BezierCurve(t: t, p0: p0, c1: c1, p1: p1)
length += previous.distance(to: current)
previous = current
}
return length
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment