先日ラーメン屋で子供がお子様セットを頼んだら、おまけでその定規を貰って懐かしくなったので、プログラムで描けないか試してみました。
言葉で説明するのは難しいのでGIFアニメーションを引用します。
(画像の著作権者:MichaelFreyさん、ライセンス:CC BY-SA 3.0、https://ja.wikipedia.org/wiki/スピログラフ )
スピログラフって言うんですね。
(この定規に世界の鉛筆が何本消費されたのか考えただけでも恐ろしいです。)
大きな円の内側を中を小さい円が自転しながら周り(ただし公転とは逆回転)、小さい円の中心から少し離れた場所を描画地点とすれば良さそうです。
早速Playgroundで書いてみました。
import UIKit
import PlaygroundSupport
/// 受け側の半径
let largeRadius = CGFloat(150)
/// ぐるぐる回す方の半径
let smallRadius = CGFloat(65.0)
/// ぐるぐる回す方の穴とセンターとの距離(0度方向)
let smallHoleDistance = CGFloat(48)
/// 回転数
let numberOfRotations = 30
/// 1周を何等分するか
let dividing = 100
/// π
let pi = CGFloat(M_PI)
let imageSize = CGSize(width: 350, height: 350)
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
let ctx = UIGraphicsGetCurrentContext()!
/// 面倒なので、中心位置が(0, 0)で、Y軸の向きも算数の教科書っぽくしておきます。
ctx.translateBy(x: imageSize.width / 2, y: imageSize.height / 2)
ctx.scaleBy(x: 1, y: -1)
/// 円周を求めます。
func circumference(radius: CGFloat) -> CGFloat {
return radius * 2 * pi
}
/// 小さい円が1回公転するときに何回自転するかを求めておきます。
let ratio = circumference(radius: largeRadius) / circumference(radius: smallRadius)
ctx.move(to: CGPoint(x: largeRadius - smallRadius + smallHoleDistance, y: 0))
for rot in 0..<numberOfRotations {
for i in 0..<dividing {
// 小さい円の中心位置を求めます。
let angle = 2 * pi / CGFloat(dividing) * CGFloat(i)
let r = largeRadius - smallRadius
let x = cos(angle) * r
let y = sin(angle) * r
// 小さい円がどれぐらい自転しているかを算出します(公転と自転は逆回り)。
let angle2 = -(angle + CGFloat(rot) * 2 * pi) * ratio
// 鉛筆を刺す穴の位置を求めます
let x2 = x + cos(angle2) * smallHoleDistance
let y2 = y + sin(angle2) * smallHoleDistance
ctx.addLine(to: CGPoint(x: x2, y: y2))
}
}
UIColor.red.setStroke()
ctx.strokePath()
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let view = UIImageView(image: img)
view.backgroundColor = .white
PlaygroundPage.current.liveView = view
パラメーターをいじると、様々な図形が描けます。
/// ぐるぐる回す方の半径
let smallRadius = CGFloat(42.0)
/// ぐるぐる回す方の穴とセンターとの距離(0度方向)
let smallHoleDistance = CGFloat(20)
/// 回転数
let numberOfRotations = 10
皆さんもいろんな図形を描いてみてください😃