openrndr-hpgl/src/main/kotlin/com/martianwabbit/hpglWriter.kt

99 lines
3.0 KiB
Kotlin

package com.martianwabbit
import org.openrndr.math.Vector2
import org.openrndr.shape.*
import java.io.File
import java.util.logging.Logger
import kotlin.math.pow
import kotlin.math.sqrt
val logger = Logger.getLogger("")
fun Composition.saveToHPGL(file: File) {
HPGL().generateFromComposition(this)
}
class HPGL(pageSize: PageSize = PageSize.A4) {
fun generateFromComposition(composition: Composition) {
composition.root.traverse { shape ->
if (shape == Stage.Before) {
if (this is ShapeNode) {
this.shape.contours.forEach { shape ->
val i = shape.sampleEquidistant(200)
val rr = StringBuilder()
for (v in i.segments) {
rr.appendln("${v.start.x}, ${v.start.y}")
}
calculateSpline(shape.segments)
}
}
}
}
}
}
private fun Vector2.distanceTo(point: Vector2): Double {
return sqrt((point.x - this.x).pow(2.0) + (point.y - this.y).pow(2.0))
}
enum class PageSize {
A4,
A3
}
private enum class Stage {
Before,
After
}
private fun CompositionNode.traverse(cb: CompositionNode.(stage: Stage) -> Unit) {
this.cb(Stage.Before)
if (this is GroupNode) {
this.children.forEach { it.traverse(cb) }
}
}
// These default values correspond to a centripetal Catmull-Rom spline
private fun calculateSpline(segments: List<Segment>, resolution: Int = 100, alpha: Double = .5, tension: Double = .0) {
val result = mutableListOf<Vector2>()
segments.forEach {
val points = mutableListOf<Vector2>()
points.add(it.start)
points.addAll(it.control)
points.add(it.end)
when(points.size) {
4 -> { // Catmull-Rom
val t0 = 0.0
val t1 = t0 + points[0].distanceTo(points[1]).pow(alpha)
val t2 = t1 + points[1].distanceTo(points[2]).pow(alpha)
val t3 = t2 + points[2].distanceTo(points[3]).pow(alpha)
val p0 = points[0]
val p1 = points[1]
val p2 = points[2]
val p3 = points[3]
val m1 = ((p1 - p0) / (t1 - t0) - (p2 - p0) / (t2 - t0) + (p2 - p1) / (t2 - t1)) * (1.0 - tension) * (t2 - t1)
val m2 = ((p2 - p1) / (t2 - t1) - (p3 - p1) / (t3 - t1) + (p3 - p2) / (t3 - t2)) * (1.0 - tension) * (t2 - t1)
val a = (p1 - p2) + m1 + m2 * 2.0
val b = ((p1 - p2) * -3.0) - m1 - m1 - m2
for (i in 0..100 step 10) {
val t = (i / 100).toDouble()
val r = a * t.pow(3) + b * t.pow(2) + m1 * t.pow(1) + p1
result.add(r)
}
}
}
}
val rr = StringBuilder()
for (v in result) {
rr.appendln("${v.x}, ${v.y}")
}
val a = Vector2(0.0, 10.0).distanceTo(Vector2(13.0, 12.0))
logger.info("done")
}