アレアレ

お役立ち情報、お悩み解決情報を発信!

xcode

swiftでXY座標に関数のグラフを描画する方法

swiftでXY座標に関数のグラフを描画する方法

今回は、XcodeのPlaygroundを使って、関数グラフを描画する方法をご紹介します。

Playgroundには、元々タイムライン上に実行中のループの値をグラフとして表示する機能があります。ですが、そこを敢えて、自分でXY座標上に描画する方法をご紹介したいというのが、今回の趣旨です。

で、この記事のトップの画像が、今回ご紹介したいグラフの描画をPlaygroundで実行している様子です。このように数学でよくみる感じの、原点を中心としたX軸とY軸の平面上にグラフを描画する方法をご紹介します。

動作環境

はじめに、今回ご紹介する方法の動作確認が取れている環境をご紹介すると、次の通りです。

  • OS: OS X El Capitan (Version 10.11.5)
  • Xcode: 7.3.1 (7D1014)
  • swift: 2.2 (swiftlang-703.0.18.8 clang-703.0.31)

新しいPlaygroundを作成し環境を整える

今回のグラフの描画方法を試すにあたり、下準備をします。まず、Xcodeを起動したら、新しいPlaygroundを作成してください。その際、Platformは、「iOS」を選択します。

新しいPlaygroundができたら、次のコードをコピペして保存してください。ちょっと長めですが、重要なのは、「//viewの定義」の行から始まる部分です。

import UIKit

public class Graph: UIView{
    
    //グラフ描画に実行する関数配列
    var functions : [(Double) -> Double] = []
    
    public init(frame: CGRect, function: (Double) -> Double) {
        self.functions.append(function)
        super.init(frame: frame)
    }
    
    public init(frame: CGRect, functions: [(Double) -> Double]) {
        self.functions = functions
        super.init(frame: frame)
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    //設定されたグラフを実行しその値に点を描画する
    override public func drawRect(rect:CGRect){
        
        let origin = CGPointMake(rect.width/2, rect.height/2)
        
        var x = rect.width/2 * (-1)
        
        while x <= rect.width/2 {
            
            for f in functions {
                
                //関数の実行
                let y = -1.0 * f(Double(x))
                
                //点をプロットする
                let r :CGFloat = 1
                let oval = UIBezierPath(ovalInRect: CGRectMake(CGFloat(x) + origin.x - r / 2, CGFloat(y) + origin.y - r / 2, r, r))
                UIColor.blueColor().setFill()
                oval.fill()
            }
            
            x += 0.1
        }
    }
}

public class Grid: UIView{
    
    //原点とx軸、y軸を描画する
    override public func drawRect(rect: CGRect) {
        
        let origin = CGPointMake(rect.width/2, rect.height/2)
        
        //原点の丸
        let r :CGFloat = 6
        let oval = UIBezierPath(ovalInRect: CGRectMake(origin.x - r / 2, origin.y - r / 2, r, r))
        UIColor.blackColor().setFill()
        oval.fill()
        
        //x軸
        drawLine(CGPointMake(origin.x, 0), end: CGPointMake(origin.x, rect.height), lineWidth: 1)
        
        //y軸
        drawLine(CGPointMake(0, origin.y), end: CGPointMake(rect.width, origin.y), lineWidth: 1)
        
    }
    
    //始点・終点間に直線を引く
    private func drawLine(start:CGPoint, end:CGPoint, lineWidth: CGFloat){
        let line = UIBezierPath()
        line.moveToPoint(start)
        line.addLineToPoint(end)
        line.closePath()
        UIColor.blackColor().setStroke()
        line.lineWidth = lineWidth
        line.stroke()
    }
    
    //O、X、Yのラベルを表示する
    public func showLabels(rect:CGRect){
        
        let origin = CGPointMake(rect.width/2, rect.height/2)
        
        //原点:O
        let oLabel = UILabel(frame: CGRectMake(rect.width/2+2, rect.height/2+2, 14, 14))
        oLabel.text = "O"
        self.addSubview(oLabel)
        
        //x
        let xLabel = UILabel(frame: CGRectMake(rect.width-16, origin.y, 14, 14+2))
        xLabel.text = "X"
        self.addSubview(xLabel)
        
        //y
        let yLabel = UILabel(frame: CGRectMake(origin.x+2, 0, 14, 14))
        yLabel.text = "Y"
        self.addSubview(yLabel)
    }
}

//viewの定義
let size = CGSize(width: 300, height: 300)
let view:UIView = UIView(frame: CGRect(origin: CGPointZero, size: size))

//x軸、y軸の表示
let grid = Grid(frame: view.frame)
grid.backgroundColor = UIColor.whiteColor()
grid.showLabels(view.frame)

view.addSubview(grid)

//グラフに表示したい関数の定義
func f1(x:Double)->Double{
    return x
}

func f2(x:Double)->Double{
    return sqrt(10000-pow(x,2)) * -1
}

//グラフの定義
let graph = Graph(frame: view.frame, functions: [f1])
graph.backgroundColor = UIColor.clearColor()
view.addSubview(graph)

//プレビュー表示用の変数
let preview = view

コードをコピペして処理が正常に実行されると、このプログラムで描画したグラフがPlayground上で確認できます。最終行の「let preview = view」の右端にある「UIView」の目玉アイコンをクリックしてみてください。

次の図のようにy=xのグラフが表示されてることが確認できるはずです。

ss1

描画するグラフを変更する

上でy=xのグラフが表示されているのは、上で紹介したコードにおいて、「f1」の関数がそのように定義されていたからです。具体的には、次の箇所ですね。

//グラフに表示したい関数の定義
func f1(x:Double)->Double{
    return x
}

これを例えばxを2乗する関数に変更するには、次のようにコードを修正します。

//グラフに表示したい関数の定義
func f1(x:Double)->Double{
    return pow(x,2)
}

このようにf1の定義を変更することで描画する関数を変更することができます。

ss2

複数の関数を組み合わせて円を描く

今回の方法では、複数の関数のグラフを描画することもできます。で、例えば円の方程式をYについて解いたものを、正と負の関数それぞれを描画することで、円を描くこともできます。

具体的には、次のようにコードを変更します。

//グラフに表示したい関数の定義
func f1(x:Double)->Double{
    return sqrt(10000-pow(x,2)) //半径100の円の上半分
}

func f2(x:Double)->Double{
    return sqrt(10000-pow(x,2)) * -1 //半径100の円の下半分
}

//グラフの定義
let graph = Graph(frame: view.frame, functions: [f1,f2]) //f2を追加

このコードの変更がうまくいくと、次のように円を描画することができます。

ss3

以上のような感じで、複数の関数をグラフに重ねて表示することもできます。

Return Top