1. 貝塞爾曲線的原理
以三階為例:設P0浦楣、P02袖肥、P2是一條拋物線上順序三個不同的點。過P0和P2點的兩切線交于P1點振劳,在P02點的切線交P0P1和P2P1于P01和P11椎组,則如下比例成立:
這是所謂拋物線的三切線定理。
當P0历恐,P2固定寸癌,引入?yún)?shù)t,令上述比值為t:(1-t)弱贼,即有:
當P0蒸苇,P2固定,引入?yún)?shù)t吮旅,令上述比值為t:(1-t)溪烤,即有:
t從0變到1,第一庇勃、二式就分別表示控制二邊形的第一氛什、二條邊,它們是兩條一次Bezier曲線匪凉。將一枪眉、二式代入第三式得:
當t從0變到1時,它表示了由三頂點P0再层、P1贸铜、P2三點定義的一條二次Bezier曲線堡纬。
并且表明:
這二次Bezier曲線P02可以定義為分別由前兩個頂點(P0,P1)和后兩個頂點(P1,P2)決定的一次Bezier曲線的線性組合。
依次類推蒿秦,
由四個控制點定義的三次Bezier曲線P03可被定義為分別由(P0,P1,P2)和(P1,P2,P3)確定的二條二次Bezier曲線的線性組合烤镐,由(n+1)個控制點Pi(i=0,1,...,n)定義的n次Bezier曲線P0n可被定義為分別由前、后n個控制點定義的兩條(n-1)次Bezier曲線P0n-1與P1n-1的線性組合:
由此得到Bezier曲線的遞推計算公式
Bézier curve(貝塞爾曲線)是應用于二維圖形應用程序的數(shù)學曲線棍鳖。 曲線定義:起始點炮叶、終止點(也稱錨點)、控制點渡处。通過調整控制點镜悉,貝塞爾曲線的形狀會發(fā)生變化。 1962年医瘫,法國數(shù)學家Pierre Bézier第一個研究了這種矢量繪制曲線的方法侣肄,并給出了詳細的計算公式,因此按照這樣的公式繪制出來的曲線就用他的姓氏來命名醇份,稱為貝塞爾曲線稼锅。
以下公式中:B(t)為t時間下 點的坐標;
P0為起點,Pn為終點,Pi為控制點
一階貝塞爾曲線(線段):
意義:由 P0 至 P1 的連續(xù)點僚纷, 描述的一條線段
二階貝塞爾曲線(拋物線):
原理:
由 P0 至 P1 的連續(xù)點 Q0矩距,描述一條線段。
由 P1 至 P2 的連續(xù)點 Q1怖竭,描述一條線段剩晴。
由 Q0 至 Q1 的連續(xù)點 B(t),描述一條二次貝塞爾曲線侵状。
經(jīng)驗:P1-P0為曲線在P0處的切線赞弥。
三階貝塞爾曲線:
通用公式:
高階貝塞爾曲線:
4階曲線:
5階曲線:
貝塞爾曲線的推到過程:
2. 用Swift進行貝塞爾曲線繪制
2.1 普通線條繪制
用Swift進行普通線條的繪制代碼,如下:
import UIKit
class BezierPathView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
self.drawLine()
self.drawCommonCurve()
self.drawSmoothPath()
}
func drawLine() {
let offset:CGFloat = 50.0
// 繪制矩形
//let rectpath = UIBezierPath(rect: CGRect.init(x: 15, y: offset, width: 300, height: 60))
let rectpath = UIBezierPath(roundedRect: CGRect.init(x: 15, y: offset, width: 300, height: 60), cornerRadius: 5.0)
rectpath.lineWidth = 5.0
UIColor.green.setStroke()
rectpath.stroke()
UIColor.red.setFill()
rectpath.fill()
// 繪制直線
let path = UIBezierPath()
path.move(to: CGPoint(x: 25.0, y: offset + 130.0))
path.addLine(to: CGPoint(x: 300.0, y: offset + 130.0))
path.lineWidth = 5.0
UIColor.cyan.setStroke()
path.stroke()
}
func drawCommonCurve() {
let offset:CGFloat = 260.0
let curvePath = UIBezierPath()
curvePath.move(to: CGPoint(x: 30.0, y: offset))
curvePath.addQuadCurve(to: CGPoint(x: 350.0, y: offset), controlPoint: CGPoint(x: 350.0, y: offset + 100))
UIColor.blue.setStroke()
curvePath.stroke()
}
func drawSmoothPath() {
let offset:CGFloat = 430
let pointCount:Int = 4
let pointArr:NSMutableArray = NSMutableArray.init()
for i in 0...pointCount {
let px: CGFloat = 15 + CGFloat(i) * CGFloat(80)
let py: CGFloat = i % 2 == 0 ? offset - 60 : offset + 60
let point: CGPoint = CGPoint.init(x: px, y: py)
pointArr.add(point)
}
let bezierPath = UIBezierPath()
bezierPath.lineWidth = 2.0
var prevPoint: CGPoint!
for i in 0 ..< pointArr.count {
let currPoint:CGPoint = pointArr.object(at: i) as! CGPoint
// 繪制綠色圓圈
let arcPath = UIBezierPath()
arcPath.addArc(withCenter: currPoint, radius: 3, startAngle: 0, endAngle: CGFloat(2 * Double.pi), clockwise: true)
UIColor.green.setStroke()
arcPath.stroke()
// 繪制平滑曲線
if i==0 {
bezierPath.move(to: currPoint)
}
else {
let conPoint1: CGPoint = CGPoint.init(x: CGFloat(prevPoint.x + currPoint.x) / 2.0, y: prevPoint.y)
let conPoint2: CGPoint = CGPoint.init(x: CGFloat(prevPoint.x + currPoint.x) / 2.0, y: currPoint.y)
bezierPath.addCurve(to: currPoint, controlPoint1: conPoint1, controlPoint2: conPoint2)
}
prevPoint = currPoint
}
UIColor.red.setStroke()
bezierPath.stroke()
}
}
運行結果: