Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Spline Interpolation In Swift
extension UIBezierPath {
/// https://github.com/jnfisher/ios-curve-interpolation/blob/master/Curve%20Interpolation/UIBezierPath%2BInterpolation.m
func catmullRomInterpolate(with points: [CGPoint], alpha: CGFloat = 0.3) {
guard points.count >= 4 else { return self.hermiteInterpolate(with: points, alpha: alpha) }
let startIndex = 1
let endIndex = points.count - 2
for currentIndex in 1..<endIndex {
let prevIndex = currentIndex - 1
let nextIndex = currentIndex + 1
let nextNextIndex = currentIndex + 2
let p0 = points[prevIndex]
let p1 = points[currentIndex]
let p2 = points[nextIndex]
let p3 = points[nextNextIndex]
func distance(_ p0: CGPoint, _ p1: CGPoint) -> CGFloat {
let dx = p0.x - p1.x
let dy = p0.y - p0.y
return sqrt(dx * dx + dy * dy)
}
let d1 = distance(p1, p0)
let d2 = distance(p2, p1)
let d3 = distance(p3, p2)
var b1: CGPoint
if abs(d1) < 1.0e-5 {
b1 = p1
} else {
b1 = CGPoint(
x: p2.x * pow(d1, 2 * alpha),
y: p2.y * pow(d1, 2 * alpha)
)
b1 = CGPoint(
x: b1.x - p0.x * pow(d2, 2 * alpha),
y: b1.y - p0.y * pow(d2, 2 * alpha)
)
b1 = CGPoint(
x: b1.x + p1.x * (2 * pow(d1, 2 * alpha) + 3 * pow(d1, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha)),
y: b1.y + p1.y * (2 * pow(d1, 2 * alpha) + 3 * pow(d1, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha))
)
b1 = CGPoint(
x: b1.x * 1.0 / (3 * pow(d1, alpha) * (pow(d1, alpha) + pow(d2, alpha))),
y: b1.y * 1.0 / (3 * pow(d1, alpha) * (pow(d1, alpha) + pow(d2, alpha)))
)
}
var b2: CGPoint
if abs(d3) < 1.0e-5 {
b2 = p2
} else {
b2 = CGPoint(
x: p1.x * pow(d3, 2 * alpha),
y: p1.y * pow(d3, 2 * alpha)
)
b2 = CGPoint(
x: b2.x - p3.x * pow(d2, 2 * alpha),
y: b2.y - p3.y * pow(d2, 2 * alpha)
)
b2 = CGPoint(
x: b2.x + p2.x * (2 * pow(d3, 2 * alpha) + 3 * pow(d3, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha)),
y: b2.y + p2.y * (2 * pow(d3, 2 * alpha) + 3 * pow(d3, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha))
)
b2 = CGPoint(
x: b2.x * 1.0 / (3 * pow(d3, alpha) * (pow(d3, alpha) + pow(d2, alpha))),
y: b2.y * 1.0 / (3 * pow(d3, alpha) * (pow(d3, alpha) + pow(d2, alpha)))
)
}
if currentIndex == startIndex {
self.move(to: p1)
}
self.addCurve(to: p2, controlPoint1: b1, controlPoint2: b2)
}
}
}
extension UIBezierPath {
/// https://github.com/FlexMonkey/SmoothScribble/blob/master/SmoothScribble/extensions/UIBezierPathExtension.swift
func hermiteInterpolate(with points: [CGPoint], alpha: CGFloat = 1 / 3) {
guard !points.isEmpty else { return }
self.move(to: points[0])
let n = points.count - 1
for index in 0..<n {
var currentPoint = points[index]
var nextIndex = (index + 1) % points.count
var prevIndex = index == 0 ? points.count - 1 : index - 1
var previousPoint = points[prevIndex]
var nextPoint = points[nextIndex]
let endPoint = nextPoint
var mx: CGFloat
var my: CGFloat
if index > 0 {
mx = (nextPoint.x - previousPoint.x) / 2
my = (nextPoint.y - previousPoint.y) / 2
} else {
mx = (nextPoint.x - currentPoint.x) / 2
my = (nextPoint.y - currentPoint.y) / 2
}
let controlPoint1 = CGPoint(x: currentPoint.x + mx * alpha, y: currentPoint.y + my * alpha)
currentPoint = points[nextIndex]
nextIndex = (nextIndex + 1) % points.count
prevIndex = index
previousPoint = points[prevIndex]
nextPoint = points[nextIndex]
if index < n - 1 {
mx = (nextPoint.x - previousPoint.x) / 2
my = (nextPoint.y - previousPoint.y) / 2
} else {
mx = (currentPoint.x - previousPoint.x) / 2
my = (currentPoint.y - previousPoint.y) / 2
}
let controlPoint2 = CGPoint(x: currentPoint.x - mx * alpha, y: currentPoint.y - my * alpha)
self.addCurve(to: endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.