Swift Playgroundsで学ぶiOSプログラミング

最新言語で古典的なグラフィック処理を試してみる

文●柴田文彦 編集●吉田ヒロ

2017年02月06日 17時00分

円弧のパスを作る

 前回に紹介した直線を作るメソッドがaddLine、ベジェ曲線がaddCurveだったことを思い出せば、円弧のパスを作るためのメソッドの名前も容易に類推できるでしょう。それはaddArcです。このメソッドも、前回に登場したmove、addLine、addCurveと組み合わせて使うことができます。まず空のパスを作ってから、始点をどこかにmoveして、そこから直線、曲線、円弧をパスに追加していくのです。

 addCurveメソッドの取る引数は、直感的でわかりやすいものになっています。紙の上にコンパスで円弧を描くことを想像すれば、自然に理解できるものばかりです。必要なのは、まず円の中心、半径、円弧を描き始める角度、描くのを止める角度、そして回転方向です。基本形は以下のようになっています。

func addArc(withCenter center: CGPoint,
radius: CGFloat,
startAngle: CGFloat,
endAngle: CGFloat,
clockwise: Bool)

 ちょっとだけ注意しなければならないのは、開始角度と終了角度は、度(degree)ではなく、ラジアン(radian)で指定することです。180度がπ、360度が2πという、アレですね。あと、回転方向は時計回りの場合はtrue、反時計回りの場合にはfalseを指定することになっています。

 さっそく試してみましょう。直線と組み合わせて扇型のパスを作り、外形線を描いてみます。

円弧のパスを描くaddArcメソッドと、直線パスを描くaddLineを組み合わせて、青木型のパスを作成してみました

 前回と同様、最初に外部モジュールをインポートする部分と、定義したビューをビューコントローラーごと、プレイグランドに張り付ける部分のコードは画面に表示されていません。画面に見えている部分の前には、

import UIKit
import PlaygroundSupport

の2行が、見えている部分のあとには、

let pgvc = UIViewController()
pgvc.view = PGView()
pgvc.view.backgroundColor = UIColor.white
PlaygroundPage.current.liveView = pgvc

という4行のコードがあります。これは、今回示すすべての画面に共通のものです。

 ここでは、まず、moveメソッドでx, y座標が(500, 380)の位置に始点を移動し、そこから右上に(640, 240)の点まで、addLineで直線を引きます。その後addArcメソッドで円弧を描き、closeメソッドでパスを閉じます。それによって、円弧の端から始点まで、自動的に直線のパスが追加されます。その後は線幅を8.0に、色をblueに設定して外形線を描いています。

 円弧の開始角度は1.75、終了角度は1.25になっていることに注目してください。角度は右方向が0で、そこから時計回りに測るので、ラジアンの1.75は右上(北東)、1.25は左上(北西)の方向になります。また、回転方向(clockwise)にはfalseを指定しているので、反時計回りになります。つまり右上から左上に向かって円弧が描かれることになります。

 ちょっと考えると、開始角度と終了角度を指定すれば、回転方向はいらないのではと思えるかもしれません。もちろんそんなことはありません。上の例で回転方向にtrueを指定して時計回りにしただけで、円弧は上の例とちょうど反対側に描かるようになります。

先ほどの図とまったく同じ座標データを使って描いても、円弧の回転方向を変えると、円の反対側を切り取った図形になります

同心円を塗りつぶす

 はじめに述べたように、パスは基本的には一筆書きですが、複数のパスを組み合わせる(結合する)ことによって、一筆書きでは描けない、離れたパスを1つのパスとして扱うことも可能です。せっかくaddArcメソッドの使い方がわかったので、2つの円を組み合わせて、半径の異なる同心円を1つのパスにしてみましょう。

 そのためには、まず中心が同じで半径だけが異なる2つの円のパスを描きます。この場合、開始角度と終了角度は、それぞれ0.0と2.0にします。回転角度はどちらでも同じで、円弧がちょうど一周回って閉じた円になります。回転方向は両方とも時計回り(true)にしておきます。そのあとで、片方のパスのappendメソッドにもう片方のパスを引数として指定すれば、最初のパスはそれ自身に引数に指定したパスが追加されたパスになります。コードと実行結果で確認してください。

単に2つの円のパスを描いただけのように見えますが、これは2つの円で1つのパスになっています。外側のパスに内側のパスを継ぎ足すことで実現しています

 ここでは、外側の円(pgPath1)に、内側の円(pgPath2)を追加しました。そのpgPath1のstrokeメソッドで外形線を描くと、二重の同心円となります。

 それでは、この2つの円を結合したpgPath1をfillメソッドで塗りつぶしたらどうなるでしょうか。strokeをfillに変えて試してみましょう。

二重の同心円のパスも、fillでそのまま塗りつぶすと、内側の円のパスも含めて塗りつぶされてしまいます

 この結果は、外側の円が内側の円もろとも塗りつぶされたものとなりました。これでは、元の外側の円だけを塗りつぶしたものと変わらないという不満を感じる人もいるでしょう。確かに内側の円のパスを追加した意味がありませんね。実は、内側の円を生かして、ドーナッツ型のように塗りつぶす方法もあります。それも、上のプログラムの1カ所を変えるだけで実現できます。

 それは、外側の円と内側の円の回転方向を反対にするだけでいいのです。内側の円のclockwiseの引数の値をfalse(反時計回り)に設定して試してみましょう。

外側の円と内側の円のパスの回転方向を逆にすると、内側の円の内側が塗りつぶされずに残るドーナッツ型になります

 結果は、外側の円と内側の円に囲まれた部分だけが塗りつぶされ、見事にドーナッツ型が完成しました。これで2つのパスを結合した意味があるというものです。なお、この場合は外側の円を反時計回りに、内側の円を時計回りに設定しても結果は同じです。

 回転方向の違いによって、なぜこのような結果の違いが出るのか疑問に思う人もいるでしょう。しかし、これはそうなるように作られているから、としか言えません。このような効果を持つパスは、円だけではありませんが、複数のパスによって囲まれた範囲を交互に塗りつぶせるように、このような性格を持たせたと考えるべきでしょう。

 ところで、今回描いている円が、まん丸の真円ではなく、かなり縦長の楕円になっていることは、誰の目にも明らかです。これは大げさに言えば平面が歪んでいるというか、縦横比が1対1になっていないために起こることです。座標の値と位置の関係や、線の太さの変化などから、もっと前に気づいていた人もいるでしょう。

 これについては詳しく説明を始めると長くなるので、また別の機会を設けたいと思います。今は、1つのビュー全体をプレイグランドの右側の表示エリアにぴったり収まるようにするために、縦横比を変えて拡縮していると理解しておいてください。iPad本体の向きを変えて縦長に持つと、画面の上側に横長の表示エリアが配置されますが、すると円も横長になることからこの説明で納得していただけると思います。

星形を塗りつぶす2つの方法

 今回の最後に、オプションの設定によって結果が異なる塗りつぶし方法をもう1つ示します。

 まず、直線のパスを5本組み合わせて(最後の1本は、パスを閉じることで自動的に補っています)星型のパスを描きます。strokeメソッドによって外形線を描けば、直線同士が交差している様子がよくわかります。

複数の直線を組み合わせて星型のパスを構成しました。最後にcloseメソッドを実行しているので、完全に閉じた1つのパスになっています

 まったく同じパスから、塗りつぶした星型を描いてみましょう。strokeをfillに変えるだけです。

上の星型のパスをそのまま塗りつぶすと、パスが交差している部分には関係なく、全体が一様に星型として塗りつぶされます

 この場合、交差しているパスの部分も、すべて塗りつぶされてしまい、星型の外形線にそって10本の直線パスを組み合わせて描いた場合と区別できません。

 ここで、パス自体の属性として、usesEvenOddFillRuleにtrueを代入すると、塗りつぶし結果は大きく異なります。パスの交差を生かして、内側の五角形部分が塗りつぶされずに残ります。

パスの属性の1つ、usesEvenOddFillRule(偶数奇数塗りつぶしルールを使う)をtrueに設定すると、パスの交差を考慮して内側の五角形の部分を塗り残した星型になります

 この属性は、複数の交差したパスから構成される図形を、なるべく「交互に」塗りつぶすために考えられたものです。これも古典的なコンピューターグラフィックの機能の1つなのです。

次回の予定

 次回は、今回のグラフィックシリーズのしめくくりとして、塗りつぶしではなく、パスをストロークする際の形に関するオプションについて見ていきます。細い線では気付きませんが、ストロークの両端や、パスが折れ曲がっている部分の形状には様々なバリエーションがあります。またストロークは1本の直線ではなく、破線や鎖線として描くこともできるのです。

mobileASCII.jp TOPページへ