随分前にGroovy で SVG を出力して、六角形のフラクタル図形を書いたというエントリーを書いたのですが、 それを Kotlin に移植しました。 あとで Haskell にも移植するつもりなので、そのとき移植しやすいように Haskell に寄せて記述したつもりです。
Groovyのコードでは、描画色をカラフルにしていましたが、これは白黒です。
// hexagon.main.kts
import java.io.File
data class Color(val red:Int, val green:Int, val blue:Int)
data class Point(val x:Float, val y:Float)
data class Circle(val centerPt: Point, val r: Float)
data class Rect(val left: Float, val top: Float, val right: Float, val bottom: Float)
typealias OneHexagonSVG = String
typealias Hexagon = List<Point>
typealias Radius = Float
val createSVG: (Rect, String)->String = { rect, svgContents->
val list = mutableListOf<String>()
list.add("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
list.add("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">")
list.add("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" x=\"${rect.left.toInt()}\" y=\"${rect.top.toInt()}\" width=\"${rect.right.toInt()}\" height=\"${rect.bottom.toInt()}\" viewBox=\"${rect.left.toInt()} ${rect.top.toInt()} ${rect.right.toInt()} ${rect.bottom.toInt()}\">")
list.add("<g style=\"stroke:rgb(0,0,0)\" stroke-width=\"1\" fill=\"black\">")
list.add("<rect x=\"${rect.left.toInt()}\" y=\"${rect.top.toInt()}\" width=\"${rect.right.toInt()}\" height=\"${rect.bottom.toInt()}\"/>")
list.add("</g>")
list.add(svgContents)
list.add("</svg>")
list.joinToString(System.getProperty("line.separator"))
}
val toHexagon: (Rect)->Hexagon = { rect->
val centerPt = Point((rect.left + rect.right)/2f, (rect.top + rect.bottom)/2f)
val vertexCount = 6
val radius = Math.min((rect.right-rect.left), (rect.bottom-rect.top))/2f
val angle = 2 * Math.PI/vertexCount
0.until(vertexCount).map {
val x = radius*Math.sin(angle*it)
val y = radius*Math.cos(angle*it)*(-1)
Point(
x.toFloat() +centerPt.x,
y.toFloat() +centerPt.y)
}
}
val toRect: (Circle)->Rect = { c->
val left = c.centerPt.x - c.r
val top = c.centerPt.y - c.r
val right = c.centerPt.x + c.r
val bottom = c.centerPt.y + c.r
Rect(left,top,right,bottom)
}
val toOneHexagonSVG: (Circle, Color) -> OneHexagonSVG = {c, strokeColor->
val list = mutableListOf<String>()
list.add("<g style=\"stroke:rgb(${strokeColor.red},${strokeColor.green},${strokeColor.blue})\" stroke-width=\"1\" fill=\"none\">")
list.add("<path d=\"")
val commandList = listOf("M", "L", "L", "L", "L", "L")
list.add(commandList.zip(toHexagon(toRect(c))).map { pair->
val cmd = pair.first
val pt = pair.second
"${cmd} ${pt.x},${pt.y}"
}.joinToString(" "))
list.add(" z\"/>")
list.add("</g>")
list.joinToString("")
}
// この関数は再帰するので fun で記述.
fun toHexagonSVGList(c: Circle, strokeColor: Color, downScale: Float): List<OneHexagonSVG>{
return if( c.r<2f ){
listOf<OneHexagonSVG>()
} else {
val oneHexagonSVG = toOneHexagonSVG(
Circle(c.centerPt, c.r*downScale),
strokeColor)
val childHexagonSVGList: List<OneHexagonSVG> = toHexagon(toRect(c)).map { centerPt1->
val newCircle = Circle(centerPt1, c.r*downScale)
toHexagonSVGList(newCircle, strokeColor, downScale)
}.flatten()
listOf(listOf(oneHexagonSVG), childHexagonSVGList).flatten()
}
}
val canvasWidth = 640f
val canvasHeight = 640f
val downScale = 0.32f
val circle = Circle(
Point(canvasWidth/2f, canvasHeight/2f),
Math.min(canvasWidth, canvasHeight)*downScale)
val hexagonSVGList = toHexagonSVGList(
circle,
Color(255,255,255),
downScale)
val svg = createSVG(Rect(0f,0f,canvasWidth,canvasHeight), hexagonSVGList.joinToString(""))
File("result.svg").printWriter(Charsets.UTF_8).use { pw ->
pw.println(svg)
}
実行する kotlinc バージョン:
$ kotlinc -version
info: kotlinc-jvm 1.8.10 (JRE 11.0.17+8-post-Ubuntu-1ubuntu220.04)
実行:
$ kotlinc -script hexagon.main.kts
結果は result.svg に生成されます。