1. 三阶贝塞尔曲线
2. 算法描述
如果我们把三阶贝塞尔曲线的 P0 和 P3 视为原始数据,只要找到 P1 和 P2 两个点(我们称其为控制点),就可以根据三阶贝塞尔曲线公式,计算出 P0 和 P3 之间平滑曲线上的任意点。
现在,平滑问题变成了如何计算两个原始数据点之间的控制点的问题。步骤如下:
第1步:绿色直线连接相邻的原始数据点,计算出个线段的中点,红色直线连接相邻的中点
第2步:根据相邻两条绿色直线长度之比,分割其中点之间红色连线,标记分割点
第3步:平移红色连线,使其分割点与相对的原始数据点重合
第4步:调整平移后红色连线的端点与原始数据点的距离,通常缩减40%-80%
3. 算法实现
代码如下:
static func smoothingBezierTuples(points: Array<CGPoint>, k: CGFloat, closed: Bool) -> [(CGPoint, CGPoint, CGPoint)] {
guard points.count > 1 else { return [] }
// 第1步
var midPoints: [(CGPoint, CGPoint, CGPoint)] = []
for i in 1..<points.count {
let start = points[i-1]
let end = points[i]
let mid = CGPoint(x: (start.x + end.x)/2.0, y: (start.y + end.y)/2.0)
midPoints.append((start, end, mid))
}
if closed {
let start = points[points.count - 1]
let end = points[0]
let mid = CGPoint(x: (start.x + end.x)/2.0, y: (start.y + end.y)/2.0)
midPoints.append((start, end, mid))
}
// 第2步
var splitPoints: [(CGPoint, CGPoint, CGPoint)] = []
for i in 0..<midPoints.count {
var j = 0
if i < (midPoints.count - 1) {
j = i + 1
} else if closed {
j = 0
} else {
continue
}
let (iStart, iEnd, iMid) = midPoints[i]
let iLength = sqrt(pow(iStart.x - iEnd.x, 2) + pow(iStart.y - iEnd.y, 2))
let (jStart, jEnd, jMid) = midPoints[j]
let jLength = sqrt(pow(jStart.x - jEnd.x, 2) + pow(jStart.y - jEnd.y, 2))
let kSplit = 1.0 * iLength / (iLength + jLength)
let split = CGPoint(x: iMid.x + (jMid.x - iMid.x)*kSplit, y: iMid.y + (jMid.y - iMid.y)*kSplit)
splitPoints.append((iMid, jMid, split))
}
// 第3步
var crtPoints: [(CGPoint, CGPoint, CGPoint)] = []
for i in 0..<splitPoints.count {
let (_, vertex, _) = midPoints[i]
let (start, end, split) = splitPoints[i]
let dx = vertex.x - split.x
let dy = vertex.y - split.y
let crtStart = CGPoint(x: start.x + dx, y: start.y + dy)
let crtEnd = CGPoint(x: end.x + dx, y: end.y + dy)
let kCrtStart = CGPoint(x: crtStart.x + (vertex.x - crtStart.x)*k, y: crtStart.y + (vertex.y - crtStart.y)*k)
let kCrtEnd = CGPoint(x: crtEnd.x + (vertex.x - crtEnd.x)*k, y: crtEnd.y + (vertex.y - crtEnd.y)*k)
// (顶点,其左侧控制点,其右侧控制点)
crtPoints.append((vertex, kCrtStart, kCrtEnd))
}
return crtPoints
}
效果截图:
参考文章:
https://blog.csdn.net/fzhlee/article/details/114375724
https://blog.csdn.net/xufive/article/details/86163741
网友评论