LoginSignup
1
2

More than 3 years have passed since last update.

[Swift5]3点を通る円を計算する

Last updated at Posted at 2019-06-01

1.はじめに

今回は計算用関数の投稿です。
指定した3点を通る円を描きたいと思い、挑戦しました。
今回は例として、一番左上から一番右下まで流れるような曲線を描きます。
※iPhoneXSとかのサンプル機の待ち受けで使われてるような線です。

2.全体のソースコード

circle.swift
import UIKit

class CirclePropaty{
    var centerPoint :CGPoint = CGPoint(x: 0, y: 0)
    var radius :CGFloat = 0
}

class ViewController: UIViewController {


    override func viewDidLoad() {
        super.viewDidLoad()



        let drawView = DrawView(frame: self.view.bounds)
        self.view.addSubview(drawView)
        // Do any additional setup after loading the view.
    }



}

class DrawView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame);
        self.backgroundColor = UIColor.clear;
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {

        //3点を通る円を計算する
        var left_top : CGPoint = CGPoint(x: UIScreen.main.bounds.minX, y: UIScreen.main.bounds.minY)
        var right_bottom : CGPoint = CGPoint(x: UIScreen.main.bounds.maxX, y: UIScreen.main.bounds.maxY)
        var middle_point : CGPoint = CGPoint(x: UIScreen.main.bounds.width/3, y: UIScreen.main.bounds.height*2/3)



        var circleData : CirclePropaty = calcCircle(pointA: left_top, pointB:right_bottom, pointC: middle_point)
        var circle_center : CGPoint = circleData.centerPoint
        var circle_radius : CGFloat = circleData.radius

        // 円
        let circle = UIBezierPath(arcCenter: circle_center, radius: circle_radius, startAngle: 0, endAngle: CGFloat(Double.pi)*2, clockwise: true)
        // 内側の色
        UIColor(red: 0, green: 1, blue: 0, alpha: 0.3).setFill()
        // 内側を塗りつぶす
        //circle.fill()
        // 線の色
        UIColor(red: 0, green: 1, blue: 0, alpha: 1.0).setStroke()
        // 線の太さ
        circle.lineWidth = 2.0
        circle.stroke()
    }

    func calcCircle(pointA:CGPoint,pointB:CGPoint,pointC:CGPoint)->CirclePropaty{
    let result = CirclePropaty()

    //(原点-pointA)(原点-pointB)
    var a : CGFloat = 0
    var b : CGFloat = 0
    var c : CGFloat = 0
    a = 2 * pointB.x - 2 * pointA.x
    b = 2 * pointB.y - 2 * pointA.y
    c = pointA.x * pointA.x + pointA.y * pointA.y - pointB.x * pointB.x - pointB.y * pointB.y

    //(原点-pointA)(原点-pointC)
    var d : CGFloat = 0
    var e : CGFloat = 0
    var f : CGFloat = 0
    d = 2 * pointC.x - 2 * pointA.x
    e = 2 * pointC.y - 2 * pointA.y
    f = pointA.x * pointA.x + pointA.y * pointA.y - pointC.x * pointC.x - pointC.y * pointC.y

    result.centerPoint.x = (b * f - c * e)/(a * e - b * d)
    result.centerPoint.y = (a * f - c * d)/(d * b - a * e)

    result.radius = sqrt((pointA.x - result.centerPoint.x) * (pointA.x - result.centerPoint.x) + (pointA.y - result.centerPoint.y) * (pointA.y - result.centerPoint.y))

    return result
    }

}

3.解説

3-1.クラス定義

circle.swift
class CirclePropaty{
    var centerPoint :CGPoint = CGPoint(x: 0, y: 0)
    var radius :CGFloat = 0
}

関数の返却値として、半径と原点の位置を取得したいのでクラスを定義します。

3-2.計算関数

circle.swift
func calcCircle(pointA:CGPoint,pointB:CGPoint,pointC:CGPoint)->CirclePropaty{
    let result = CirclePropaty()

    //(原点-pointA)(原点-pointB)
    var a : CGFloat = 0
    var b : CGFloat = 0
    var c : CGFloat = 0
    a = 2 * pointB.x - 2 * pointA.x
    b = 2 * pointB.y - 2 * pointA.y
    c = pointA.x * pointA.x + pointA.y * pointA.y - pointB.x * pointB.x - pointB.y * pointB.y

    //(原点-pointA)(原点-pointC)
    var d : CGFloat = 0
    var e : CGFloat = 0
    var f : CGFloat = 0
    d = 2 * pointC.x - 2 * pointA.x
    e = 2 * pointC.y - 2 * pointA.y
    f = pointA.x * pointA.x + pointA.y * pointA.y - pointC.x * pointC.x - pointC.y * pointC.y

    result.centerPoint.x = (b * f - c * e)/(a * e - b * d)
    result.centerPoint.y = (a * f - c * d)/(d * b - a * e)

    result.radius = sqrt((pointA.x - result.centerPoint.x) * (pointA.x - result.centerPoint.x) + (pointA.y - result.centerPoint.y) * (pointA.y - result.centerPoint.y))

    return result
    }

3点を通る円はこの関数にCGPointを3つ渡してあげれば大丈夫です。
どうしてこうなるのかはググってみてください。

3-3.使用例

circle.swift
override func draw(_ rect: CGRect) {

        var left_top : CGPoint = CGPoint(x: UIScreen.main.bounds.minX, y: UIScreen.main.bounds.minY)
        var right_bottom : CGPoint = CGPoint(x: UIScreen.main.bounds.maxX, y: UIScreen.main.bounds.maxY)
        var middle_point : CGPoint = CGPoint(x: UIScreen.main.bounds.width/3, y: UIScreen.main.bounds.height*2/3)



        var circleData : CirclePropaty = calcCircle(pointA: left_top, pointB:right_bottom, pointC: middle_point)
        var circle_center : CGPoint = circleData.centerPoint
        var circle_radius : CGFloat = circleData.radius

        // 円
        let circle = UIBezierPath(arcCenter: circle_center, radius: circle_radius, startAngle: 0, endAngle: CGFloat(Double.pi)*2, clockwise: true)
        // 内側の色
        UIColor(red: 0, green: 1, blue: 0, alpha: 0.3).setFill()
        // 線の色
        UIColor(red: 0, green: 1, blue: 0, alpha: 1.0).setStroke()
        // 線の太さ
        circle.lineWidth = 2.0
        circle.stroke()
    }

自作クラスのUIViewのdrawにoverrideして、先ほど作成した関数を使って円を描いてみました。
このViewをViewControllerでaddSubviewしてあげればOKです。

4.終わりに

開発スピードを上げていきたいので説明を簡略化していこうかなと思い、今回から軽い説明に切り替えました。きっとみなさんならコードとにらめっこして理解してくれるはずと信じています。

以上。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2