タッチされた2点間の距離や方向ベクトルを計算するとき、 CGPoint をベクトルとみなして次のようにできると便利です。
let a = CGPoint(x: 1, y: 2), b = CGPoint(x: -3, y: 5)
let sum = a + b // aとbの和
let distance = (b - a).length // a、b間の距離
let direction = (b - a).unit // a→bの方向ベクトル
Swiftでは struct (構造体)に演算子を定義したり、 extension を使ってプロパティやメソッドを追加することができます。 CGPoint を次のように拡張しておくと便利です。
演算子を定義する
演算子を新たに定義する場合には、次のように、演算子の名前を持った関数を定義します。
func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
- の場合でも同じです。
* の場合は、スカラー(この場合は CGFloat )を右からかける場合と左からがあるのでオーバーロードします。
func * (left: CGPoint, right: CGFloat) -> CGPoint {
return CGPoint(x: left.x * right, y: left.y * right)
}
func * (left: CGFloat, right: CGPoint) -> CGPoint {
return CGPoint(x: right.x * left, y: right.y * left)
}
内積の計算もオーバーロードして定義します。戻り値の型が CGFloat なことに注意して下さい。
func * (left: CGPoint, right: CGPoint) -> CGFloat {
return left.x * right.x + left.y * right.y
}
ひき算ではなく、 -a のように - をつけられるようにするには、 prefix キーワードを用いて演算子を定義します。
prefix func - (value: CGPoint) -> CGPoint {
return CGPoint(x: -value.x, y: -value.y)
}
プロパティを追加する
extension を使えば、 struct に Conputational Property を追加することができます。
次のようにして、 CGPoint (をベクトルとみなした場合)の長さを得るプロパティを追加します。
extension CGPoint {
var length: CGFloat {
get {
return sqrt(self.x * self.x + self.y * self.y)
}
}
}
単位ベクトルを得るプロパティも定義しておくと便利です。
extension CGPoint {
var unit: CGPoint {
get {
return self * (1.0 / self.length)
}
}
}
メソッドを追加する
プロパティだけでなく、メソッドを追加することもできます。
ベクトルのなす角を計算するメソッドを追加してみましょう。ベクトルのなす角については
\cos \theta = \frac{\bf{a} \cdot \bf{b}}{|\bf{a}| |\bf{b}|}
が成り立つので、次のようにメソッドを定義します。
extension CGPoint {
func angleFrom(point: CGPoint) -> CGFloat {
return acos(fmin(fmax(self * point / (self.length * point.length), -1.0), 1.0))
}
}
fmin や fmax は、浮動小数点数の計算誤差で 1.0 や -1.0 をわずかに超えてしまう場合への対処です。
GitHub
せっかくなので、 GitHubに公開しました。 CGPoint+Vector.swift をプロジェクトに追加するだけで使えます。
まとめ
Swiftでは struct に演算子やプロパティ、メソッドを追加することができます。 CGPoint をベクトルとして計算をすることは多いので、 + や - の演算子や、 length などのプロパティを追加してみました。