三阶贝塞尔曲线真牛逼啊!

2024-01-01  本文已影响0人  大成小栈

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

上一篇下一篇

猜你喜欢

热点阅读