取り急ぎベジェを使った円の描画を復習したので書き残す。
circle.main.kts にコードを書く。 kotlin circle.main.kts で実行すると circle.png が生成される。
import java.io.File
import java.awt.Graphics2D
import java.awt.Color
import java.awt.geom.Point2D
import java.awt.geom.GeneralPath
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
System.setProperty("java.awt.headless", "true")
object MatrixUtils {
fun times(a: FloatArray, b: FloatArray): FloatArray {
return floatArrayOf(
a[0]*b[0] + a[1]*b[3] + a[2]*b[6],
a[0]*b[1] + a[1]*b[4] + a[2]*b[7],
a[0]*b[2] + a[1]*b[5] + a[2]*b[8],
a[0+3]*b[0] + a[1+3]*b[3] + a[2+3]*b[6],
a[0+3]*b[1] + a[1+3]*b[4] + a[2+3]*b[7],
a[0+3]*b[2] + a[1+3]*b[5] + a[2+3]*b[8],
a[0+3*2]*b[0] + a[1+3*2]*b[3] + a[2+3*2]*b[6],
a[0+3*2]*b[1] + a[1+3*2]*b[4] + a[2+3*2]*b[7],
a[0+3*2]*b[2] + a[1+3*2]*b[5] + a[2+3*2]*b[8]
)
}
fun times(a: FloatArray, b: FloatArray, c: FloatArray): FloatArray = times( a, times(b, c) )
fun times(a: FloatArray, b: FloatArray, c: FloatArray, d: FloatArray): FloatArray = times( a, times(b, times(c, d)) )
fun transform(a: FloatArray, x: Float, y: Float): FloatArray {
val b: FloatArray = floatArrayOf(x, y, 1f)
val c00 = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
val c10 = a[0 + 3] * b[0] + a[1 + 3] * b[1] + a[2 + 3] * b[2]
val c20 = a[0 + 6] * b[0] + a[1 + 6] * b[1] + a[2 + 6] * b[2]
return floatArrayOf(c00, c10, c20)
}
fun transform(a: FloatArray, pt: Point2D.Float): Point2D.Float {
val array = transform(a, pt.x, pt.y)
return Point2D.Float(array[0], array[1])
}
}
val toRadian: (Double) -> Double = { degree-> degree * Math.PI/180f }
// 原点に並行移動して、degree度分回転、元の位置へ並行移動する matrixValues を生成:
val toMatrixValues: (Double) -> FloatArray = { degree->
val radian = toRadian(degree)
val matrixValues0 = floatArrayOf(
1f, 0f, -50f,
0f, 1f, -50f,
0f, 0f, 1f)
val matrixValues1 = floatArrayOf(
Math.cos(radian).toFloat(), -Math.sin(radian).toFloat(), 0.toFloat(),
Math.sin(radian).toFloat(), Math.cos(radian).toFloat(), 0.toFloat(),
0f, 0f, 1f)
val matrixValues2 = floatArrayOf(
1f, 0f, 50f,
0f, 1f, 50f,
0f, 0f, 1f)
MatrixUtils.times(matrixValues2, matrixValues1, matrixValues0)
}
// 回転して並行移動する matrixValues を生成:
val toMatrixValuesWithTranslation: (Double, Float, Float) -> FloatArray = { degree, dx, dy->
val matrixValues = floatArrayOf(
1f, 0f, dx,
0f, 1f, dy,
0f, 0f, 1f)
MatrixUtils.times( matrixValues, toMatrixValues(degree) )
}
// 200x200 のキャンバスを作成:
val w = 200
val h = 200
val img = BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)
val g = img.getGraphics() as Graphics2D
g.setColor(Color.WHITE)
g.fillRect(0, 0, w, h)
// 中心が (100,100) 半径 R=100 の円弧 (左上1/4) を描画するための点の計算:
val M = (4f / 3f * (Math.sqrt(2.toDouble())-1f)).toFloat() /// 円をベジェで描くためのマジックナンバー
val R = 100f
val RM = R*M
val startPt = Point2D.Float(0f, R)
val controlPt1 = Point2D.Float(0f, R-RM)
val controlPt2 = Point2D.Float(R-RM, 0f)
val stopPt = Point2D.Float(R, 0f)
val ptList: List<Point2D.Float> = listOf(startPt, controlPt1, controlPt2, stopPt)
// 残りの 3つの円弧は、今作成した点をベースに回転と並行移動を使って作成:
val matrixValues1 = toMatrixValuesWithTranslation((90*1).toDouble(), 100f, 0f)
val matrixValues2 = toMatrixValuesWithTranslation((90*2).toDouble(), 100f, 100f)
val matrixValues3 = toMatrixValuesWithTranslation((90*3).toDouble(), 0f, 100f)
val ptList1 = ptList.map { MatrixUtils.transform(matrixValues1, it) }
val ptList2 = ptList.map { MatrixUtils.transform(matrixValues2, it) }
val ptList3 = ptList.map { MatrixUtils.transform(matrixValues3, it) }
// パスを使って円を描画:
val path = GeneralPath()
listOf(ptList, ptList1, ptList2, ptList3).forEach {
path.moveTo(it[0].x, it[0].y)
path.curveTo(
it[1].x, it[1].y,
it[2].x, it[2].y,
it[3].x, it[3].y)
}
g.color = Color.BLACK
g.draw(path)
// PNGとしてファイルに保存:
val pngFile = File("circle.png")
ImageIO.write(img, "PNG", pngFile)