Skip to content

Instantly share code, notes, and snippets.

@zadr
Last active December 20, 2023 05:36
Show Gist options
  • Save zadr/f829506ee451916c9eff362d23b1ccfe to your computer and use it in GitHub Desktop.
Save zadr/f829506ee451916c9eff362d23b1ccfe to your computer and use it in GitHub Desktop.
import UIKit
extension UIBezierPath {
// reference: https://github.com/erica/iOS-6-Cookbook/blob/master/C01%20Gestures/08%20-%20Smoothed%20Drawing/UIBezierPath-Points.m
var points: [CGPoint] {
var bezierPoints = [CGPoint]()
cgPath.applyWithBlock { (element: UnsafePointer<CGPathElement>) in
if element.pointee.type != .closeSubpath {
bezierPoints.append(element.pointee.points.pointee)
if element.pointee.type != .addLineToPoint && element.pointee.type != .moveToPoint {
bezierPoints.append(element.pointee.points.advanced(by: 1).pointee)
}
}
if element.pointee.type == .addCurveToPoint {
bezierPoints.append(element.pointee.points.advanced(by: 2).pointee)
}
}
return bezierPoints
}
func smoothened(granularity: Int = 24) {
smoothen(granularity: granularity, into: self)
}
// reference: https://github.com/erica/iOS-6-Cookbook/blob/master/C01%20Gestures/08%20-%20Smoothed%20Drawing/UIBezierPath-Smoothing.m
@discardableResult
func smoothen(granularity: Int = 24, into bezier: UIBezierPath? = nil) -> UIBezierPath {
var allPoints = points
guard allPoints.count > 4 else {
return self
}
allPoints.insert(allPoints.first!, at: 0)
allPoints.append(allPoints.last!)
let smoothened = bezier ?? (copy() as! UIBezierPath)
smoothened.removeAllPoints()
var hasMovedToInitialPoint = false
for i in (4 ..< allPoints.count) {
let subpoints = allPoints[i - 3 ... i]
for i in (1 ..< granularity) {
let t = CGFloat(i) * (CGFloat(1.0) / CGFloat(granularity))
let tt = t * t
let ttt = tt * t
func smoothenedCoodinateComponeny(fromNearby points: ArraySlice<CGFloat>) -> CGFloat {
// explicit types needed to compile in reasonble time
return CGFloat(0.5) *
({ CGFloat(2) * points[1] + (points[2] - points[0]) * t }() +
{ (CGFloat(2) * points[0] - CGFloat(5) * points[1] + CGFloat(4) * points[2] - points[3]) * tt }() +
{ (CGFloat(3) * points[1] - points[0] - CGFloat(3) * points[2] + points[3]) * ttt }())
}
let pX = smoothenedCoodinateComponeny(fromNearby: subpoints[keyPath: \CGPoint.x])
let pY = smoothenedCoodinateComponeny(fromNearby: subpoints[keyPath: \CGPoint.y])
if !hasMovedToInitialPoint {
hasMovedToInitialPoint = true
smoothened.move(to: CGPoint(x: pX, y: pY))
} else {
smoothened.addLine(to: CGPoint(x: pX, y: pY))
}
}
smoothened.addLine(to: p2)
}
smoothened.addLine(to: allPoints.last!)
return smoothened
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment