Last active
February 20, 2020 00:40
-
-
Save swhitty/2f2c04f56f9e36879ea0a353a99d82e9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import CoreGraphics | |
extension CGPath { | |
// Creates a smooth CGPath line between supplied points. | |
// | |
// Typical Polyline, straight line from point to point. | |
// | |
// /\ | |
// __ ___/ \ / | |
// \__/ \/ | |
// | |
// | |
// Smooth Polyline, cubic curve from point to point. | |
// | |
// ,"\ / | |
// __,' \ / | |
// "-._,' " | |
static func makeSmoothPolyline(between points: [CGPoint]) -> CGPath { | |
let path = CGMutablePath() | |
guard points.count > 1 else { | |
return path.copy()! | |
} | |
path.move(to: points[0]) | |
for i in 1..<points.count { | |
let cps = controlPoint(current: points[i - 1], | |
previous: points[safe: i - 2], | |
next: points[i], | |
rotate: 0) | |
let cpe = controlPoint(current: points[i], | |
previous: points[i - 1], | |
next: points[safe: i + 1], | |
rotate: .pi) | |
path.addCurve(to: points[i], control1: cps, control2: cpe) | |
} | |
return path.copy()! | |
} | |
private static func opposingLine(from previous: CGPoint, to next: CGPoint) -> (length: CGFloat, angle: CGFloat) { | |
let dx = next.x - previous.x | |
let dy = next.y - previous.y | |
return (length: sqrt(dx * dx + dy * dy), | |
angle: atan2(dy, dx)) | |
} | |
private static func controlPoint(current: CGPoint, previous: CGPoint?, next: CGPoint?, smoothing: CGFloat = 0.2, rotate: CGFloat) -> CGPoint { | |
let (length, angle) = opposingLine(from: previous ?? current, to: next ?? current) | |
return CGPoint(x: current.x + cos(angle + rotate) * length * smoothing, | |
y: current.y + sin(angle + rotate) * length * smoothing) | |
} | |
} | |
private extension Array where Element == CGPoint { | |
subscript(safe index: Int) -> Element? { | |
guard indices.contains(index) else { return nil } | |
return self[index] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment