Swift: macOSでの座標系のややこしい話
はじめに
iOS は常に画面左上が原点ですが、macOS の世界では原点の位置がフレームワークによって異なります。主に3つの座標系が存在し、この記事ではそれらをNSScreen座標系
、CGWindow座標系
、CGImage座標系
と呼称します。
基本認識
上で述べたように iOS の原点は基本的に左上ですが、macOS での基本的な原点は画面左下になります。
左:iOS、右:macOS
回転方向
iOS と macOS で同様のコードを書いてみて、回転方向について検証してみましょう。
import UIKit
class DrawView: UIView {
override func draw(_ rect: CGRect) {
let w = self.frame.width
let h = self.frame.height
let len = 0.5 * min(w, h)
let center = CGPoint(x: 0.5 * w, y: 0.5 * h)
let anchor1 = CGPoint(x: center.x + len * cos(0),
y: center.y + len * sin(0))
let path1 = UIBezierPath()
path1.move(to: center)
path1.addLine(to: anchor1)
path1.stroke()
let anchor2 = CGPoint(x: center.x + len * cos(CGFloat.pi / 3.0),
y: center.y + len * sin(CGFloat.pi / 3.0))
let path2 = UIBezierPath()
path2.move(to: center)
path2.addLine(to: anchor2)
path2.stroke()
}
}
実行結果:時計回り
import Cocoa
class DrawView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let w = self.frame.width
let h = self.frame.height
let len = 0.5 * min(w, h)
let center = CGPoint(x: 0.5 * w, y: 0.5 * h)
let anchor1 = CGPoint(x: center.x + len * cos(0),
y: center.y + len * sin(0))
let path1 = NSBezierPath()
path1.move(to: center)
path1.line(to: anchor1)
path1.stroke()
let anchor2 = CGPoint(x: center.x + len * cos(CGFloat.pi / 3.0),
y: center.y + len * sin(CGFloat.pi / 3.0))
let path2 = NSBezierPath()
path2.move(to: center)
path2.line(to: anchor2)
path2.stroke()
}
}
実験結果:反時計回り
本題:macOSのややこしい座標系
Mac の場合ディスプレイを複数繋ぐことがあり得るため、メインのディスプレイ(Dockが置いてあるディスプレイのこと)を中枢として座標系が構成されるようです。
NSScreen座標系
メインのディスプレイの左下を原点とした座標系です。NSWindow
やNSView
などのframeは基本的にこの座標系にしたがっています。メインのディスプレイよりも下のディスプレイに配置されたウィンドウのy座標はマイナスになるので、若干扱いづらいです。
NSScreen座標系でのマウスカーソルの位置の取得方法
let location: NSPoint = NSEvent.mouseLocation
CGWindow座標系
メインのディスプレイの左上を原点とした座標系です。一見iOSの座標系と同じように扱えて便利に思えますが、NSScreen座標系と混ざることがなかなか避けられないため、そうでもありません。CGWindow座標系はCGGetDisplaysWithPoint()
やCGWindowListCreateImage()
など、スクリーンのキャプチャを取得する時に重要です。
CGWindow座標系でのマウスカーソルの位置の取得方法
let location: CGPoint = CGEvent(source: nil)!.location
CGImage座標系
メインのディスプレイとか関係なく、一枚の画像データに関する座標系で、左下を原点としています。ただ、ピクセルレベルでの情報取得を試みる場合、アクセス方法によっては左上原点になる場合があるのでこれもまた要注意です。
終わりに
とにかく、macOS では原点が安定しないので座標系を意識しながらプログラムすることがとても大切です。画面を画像や動画としてキャプチャするようなアプリを開発する場合は、複数ディスプレイを繋いだ状態でデバッグすることをお勧めします。
Discussion