diff --git a/src/main/kotlin/TemplateProgram.kt b/src/main/kotlin/TemplateProgram.kt index 44a8cec..49f7845 100644 --- a/src/main/kotlin/TemplateProgram.kt +++ b/src/main/kotlin/TemplateProgram.kt @@ -1,9 +1,13 @@ +import com.martianwabbit.HPGL +import com.martianwabbit.logger import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.math.Vector2 import org.openrndr.shape.CompositionDrawer import java.io.File import com.martianwabbit.saveToHPGL +import org.openrndr.Configuration +import org.openrndr.Program fun main() = application { configure { @@ -17,7 +21,9 @@ fun main() = application { c.circle(Vector2(20.0, 20.0), 12.0) c.composition.saveToHPGL(File("output.plt")) - // this.application.exit() + + + this.application.exit() drawer.background(ColorRGBa.WHITE) drawer.composition(c.composition) } diff --git a/src/main/kotlin/com/martianwabbit/hpglWriter.kt b/src/main/kotlin/com/martianwabbit/hpglWriter.kt index 4a6eb15..d1dedb9 100644 --- a/src/main/kotlin/com/martianwabbit/hpglWriter.kt +++ b/src/main/kotlin/com/martianwabbit/hpglWriter.kt @@ -1,30 +1,30 @@ package com.martianwabbit -import org.openrndr.shape.Composition -import org.openrndr.shape.CompositionNode -import org.openrndr.shape.GroupNode -import org.openrndr.shape.ShapeNode +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("") -public fun Composition.saveToHPGL(file: File) { - generateHPGL(this) +fun Composition.saveToHPGL(file: File) { + HPGL().generateFromComposition(this) } -private enum class Stage { - Before, - After -} - -private fun generateHPGL(composition: Composition) { - composition.root.traverse { shape -> - if (shape == Stage.Before) { - if (this is ShapeNode) { - this.shape.contours.forEach { shape -> - shape.segments.forEach { segment -> - // Render these segments +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) } } } @@ -32,6 +32,20 @@ private fun generateHPGL(composition: Composition) { } } +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) @@ -39,3 +53,47 @@ private fun CompositionNode.traverse(cb: CompositionNode.(stage: Stage) -> Unit) this.children.forEach { it.traverse(cb) } } } + +// These default values correspond to a centripetal Catmull-Rom spline +private fun calculateSpline(segments: List, resolution: Int = 100, alpha: Double = .5, tension: Double = .0) { + val result = mutableListOf() + segments.forEach { + val points = mutableListOf() + 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") +} \ No newline at end of file