Compare commits
5 Commits
fb7613114b
...
e70009ab7f
| Author | SHA1 | Date |
|---|---|---|
|
|
e70009ab7f | |
|
|
6140d5d0d6 | |
|
|
5e2d9fccd2 | |
|
|
adce888367 | |
|
|
6c06d8ea37 |
|
|
@ -1,2 +1,2 @@
|
|||
rootProject.name = 'openrndr-template'
|
||||
rootProject.name = 'openrndr-hpgl'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import com.martianwabbit.HPGL
|
||||
import com.martianwabbit.logger
|
||||
|
||||
import com.martianwabbit.saveToHPGL
|
||||
import com.martianwabbit.selectPen
|
||||
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 {
|
||||
|
|
@ -18,10 +16,12 @@ fun main() = application {
|
|||
program {
|
||||
extend {
|
||||
val c = CompositionDrawer()
|
||||
c.circle(Vector2(20.0, 20.0), 12.0)
|
||||
|
||||
c.composition.saveToHPGL(File("output.plt"))
|
||||
c.selectPen(1)
|
||||
c.circle(Vector2(40.0, 40.0), 30.0)
|
||||
c.rectangle(75.0, 60.0, 80.0, 200.0)
|
||||
c.lineSegment(Vector2(0.0, 0.0), Vector2(200.0, 200.0))
|
||||
|
||||
c.composition.saveToHPGL(File("output.plt"), this.width, this.height)
|
||||
|
||||
this.application.exit()
|
||||
drawer.background(ColorRGBa.WHITE)
|
||||
|
|
|
|||
|
|
@ -3,42 +3,69 @@ 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)
|
||||
fun Composition.saveToHPGL(file: File, width: Int = 0, height: Int = 0) {
|
||||
val plot = HPGL(width = width, height = height).generateFromComposition(this)
|
||||
file.writeText(plot)
|
||||
}
|
||||
|
||||
class HPGL(pageSize: PageSize = PageSize.A4) {
|
||||
fun generateFromComposition(composition: Composition) {
|
||||
composition.root.traverse { shape ->
|
||||
if (shape == Stage.Before) {
|
||||
if (this is ShapeNode) {
|
||||
class HPGL(
|
||||
private val p1: Vector2 = Vector2.ZERO,
|
||||
private val p2: Vector2 = Vector2.ZERO,
|
||||
private val width: Int = 0,
|
||||
private val height: Int = 0
|
||||
) {
|
||||
fun generateFromComposition(composition: Composition): String {
|
||||
val hpgl = StringBuilder()
|
||||
|
||||
hpgl.cmd("IN")
|
||||
|
||||
// Setup plotter coordinates
|
||||
if (p1 != Vector2.ZERO || p2 != Vector2.ZERO) {
|
||||
hpgl.cmd("IP", p1, p2)
|
||||
}
|
||||
|
||||
// Setup scaling
|
||||
// If no width and height is provided it'll default to the plotter's scaling.
|
||||
// Ideally this should always be the screen width and height as that'll make it
|
||||
// so that everything scales correctly from the screensize into the page size.
|
||||
if (width == 0 && height == 0) {
|
||||
hpgl.cmd("SC")
|
||||
} else {
|
||||
hpgl.cmd("SC", Vector2(.0, width.toDouble()), Vector2(.0, height.toDouble()))
|
||||
}
|
||||
|
||||
hpgl.cmd("PU") // Raise Pen
|
||||
hpgl.cmd("PA", Vector2.ZERO) // Plot absolutely
|
||||
|
||||
composition.root.traverse { stage ->
|
||||
if (stage == Stage.Before) {
|
||||
when (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)
|
||||
val segments = shape.sampleLinear().segments
|
||||
segments.forEachIndexed { i, segment ->
|
||||
if (i == 0) {
|
||||
hpgl.cmd("PU", segment.start) // Go to shape start
|
||||
hpgl.cmd("PD", segment.end)
|
||||
} else {
|
||||
hpgl.cmd("PD", segment.start, segment.end) // PD coordinates for all other segments
|
||||
}
|
||||
}
|
||||
|
||||
hpgl.cmd("PU") // Done plotting
|
||||
}
|
||||
}
|
||||
|
||||
is SelectPen -> hpgl.cmd("SP", this.penNumber)
|
||||
is VelocitySelect -> hpgl.cmd("VS", this.velocity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Vector2.distanceTo(point: Vector2): Double {
|
||||
return sqrt((point.x - this.x).pow(2.0) + (point.y - this.y).pow(2.0))
|
||||
hpgl.cmd("PA", Vector2.ZERO)
|
||||
return hpgl.toString()
|
||||
}
|
||||
|
||||
enum class PageSize {
|
||||
A4,
|
||||
A3
|
||||
}
|
||||
|
||||
private enum class Stage {
|
||||
|
|
@ -49,51 +76,37 @@ private enum class Stage {
|
|||
private fun CompositionNode.traverse(cb: CompositionNode.(stage: Stage) -> Unit) {
|
||||
this.cb(Stage.Before)
|
||||
|
||||
if (this is GroupNode) {
|
||||
if (this is GroupNode && this.children.size > 0) {
|
||||
this.children.forEach { it.traverse(cb) }
|
||||
}
|
||||
|
||||
this.cb(Stage.After)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val Vector2.coords: String
|
||||
get() {
|
||||
return "$x,$y"
|
||||
}
|
||||
|
||||
val rr = StringBuilder()
|
||||
for (v in result) {
|
||||
rr.appendln("${v.x}, ${v.y}")
|
||||
|
||||
// HPGL Custom Instructions
|
||||
private class SelectPen(val penNumber: Int) : GroupNode()
|
||||
|
||||
fun CompositionDrawer.selectPen(pen: Int) {
|
||||
require(pen >= 0) { "The pen can only be 0 or greater." }
|
||||
this.root.children.add(SelectPen(pen))
|
||||
}
|
||||
val a = Vector2(0.0, 10.0).distanceTo(Vector2(13.0, 12.0))
|
||||
logger.info("done")
|
||||
|
||||
private class VelocitySelect(val velocity: Double) : GroupNode()
|
||||
|
||||
fun CompositionDrawer.velocitySelect(velocity: Double) {
|
||||
require(velocity in 0.0..127.9999) { "Invalid velocity value. The velocity should be a value between 0 and 127.9999" }
|
||||
this.root.children.add(VelocitySelect(velocity))
|
||||
}
|
||||
|
||||
// StringBuilder Extensions
|
||||
private fun StringBuilder.cmd(cmd: String) = this.appendln("$cmd;")
|
||||
private fun StringBuilder.cmd(cmd: String, i: Int) = this.appendln("$cmd${i};")
|
||||
private fun StringBuilder.cmd(cmd: String, i: Double) = this.appendln("$cmd${i};")
|
||||
private fun StringBuilder.cmd(cmd: String, vararg vectors: Vector2) =
|
||||
this.appendln(vectors.joinToString(separator = ",", prefix = cmd, postfix = ";") { it.coords })
|
||||
|
|
|
|||
Loading…
Reference in New Issue