しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

【Swift】ベジェ曲線を自前で描いてみる

ベジェ曲線って良く聞くんですが、イマイチ理解できてなかったので自前で描いてみました。
具体的にはベジェ曲線の座標を自分で計算して描画をしてみました。

ベジェ曲線の座標の求め方

ベジェ曲線の座標は制御点を使って求められます。
今回は下のように制御点が3つの場合のベジェ曲線を求めます。

f:id:llcc:20170504212742p:plain

まずはP1とP2、P2とP3の2点間をつなぎます。

f:id:llcc:20170504213146p:plain

次にP1-P2間で少しだけP1から離れた点P4とP2-P3で少しだけP2から離れたP5を決めて、それらをつなぎます。

f:id:llcc:20170504213445p:plain

そしてP4-P5間で少しだけP4から離れた点P6がベジェ曲線の座標になります。

f:id:llcc:20170504213602p:plain

段々とP4・P5をP2・P3に近づけて行きつつ、それぞれのP6を算出します。

f:id:llcc:20170504213755p:plain

f:id:llcc:20170504213911p:plain

ここで算出したP6を全てつなげればベジェ曲線になります。
今回はこれをSwiftで実装してみようと思います。

ベジェ曲線をSwiftで描画

今回はこの3つの制御点を使ったベジェ曲線を求めます。

f:id:llcc:20170504214739p:plain

現状のコードは下の通りです。

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myView = MyView(frame: view.bounds)
        myView.backgroundColor = UIColor.white
        view.addSubview(myView)
    }
}

class MyView: UIView {
    let point1 = CGPoint(x: 100, y: 200)
    let point2 = CGPoint(x: 200, y: 100)
    let point3 = CGPoint(x: 300, y: 200)
    
    override func draw(_ rect: CGRect) {
        UIColor.darkGray.setFill()
        UIBezierPath(roundedRect: CGRect(origin: point1, size: CGSize(width: 5, height: 5)), cornerRadius: 2).fill()
        UIBezierPath(roundedRect: CGRect(origin: point2, size: CGSize(width: 5, height: 5)), cornerRadius: 2).fill()
        UIBezierPath(roundedRect: CGRect(origin: point3, size: CGSize(width: 5, height: 5)), cornerRadius: 2).fill()
    }
}

先程描いたように、point1・point2・point3を元に、point6を求めてそれを繋いでいきます。

class MyView: UIView {
    let point1 = CGPoint(x: 100, y: 200)
    let point2 = CGPoint(x: 200, y: 100)
    let point3 = CGPoint(x: 300, y: 200)
    
    override func draw(_ rect: CGRect) {
        let count = 100
        let path = UIBezierPath()
        path.move(to: point1)
        (0...count).forEach {
            let point4 = CGPoint(
                x: point1.x + (point2.x - point1.x) * CGFloat($0) / CGFloat(count),
                y: point1.y + (point2.y - point1.y) * CGFloat($0) / CGFloat(count)
            )
            let point5 = CGPoint(
                x: point2.x + (point3.x - point2.x) * CGFloat($0) / CGFloat(count),
                y: point2.y + (point3.y - point2.y) * CGFloat($0) / CGFloat(count)
            )
            let point6 = CGPoint(
                x: point4.x + (point5.x - point4.x) * CGFloat($0) / CGFloat(count),
                y: point4.y + (point5.y - point4.y) * CGFloat($0) / CGFloat(count)
            )
            path.addLine(to: point6)
        }
        path.addLine(to: point3)
        path.lineWidth = 5.0
        UIColor.brown.setStroke()
        path.stroke()
    }
}

これを実行すると以下のようになります。
無事にきれいな曲線を引くことができました。

f:id:llcc:20170504215426p:plain