Compare commits

...

5 Commits

Author SHA1 Message Date
David Diaz e70009ab7f Removed an extra space that was added between commands. 2019-11-05 19:44:36 -06:00
David Diaz 6140d5d0d6 Moving a couple of things around. 2019-10-28 15:13:15 -06:00
David Diaz 5e2d9fccd2 Doing some cleanup and refactoring.
I've added some extension methods so that adding the commands is cleaner and less error prone (I kept forgetting the semicolons.).
2019-10-27 14:36:57 -06:00
David Diaz adce888367 Added velocitySelect, some extra checks to the custom commands. Finally settled on a scaling strategy (Stick with the default IP but scale according to a given size (this size should ideally be the screen size openrndr is using on the screen)). 2019-10-26 14:41:22 -06:00
David Diaz 6c06d8ea37 Turns out there's a built in way to get line segments out of shapes in OpenRNDR.
Using this I get less points and about the same quality as what I originally intended. Plotting seems to be working. I still need to finish setting up the coordinate system and scaling.
2019-10-26 02:46:43 -06:00
3 changed files with 91 additions and 78 deletions

View File

@ -1,2 +1,2 @@
rootProject.name = 'openrndr-template' rootProject.name = 'openrndr-hpgl'

View File

@ -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.application
import org.openrndr.color.ColorRGBa import org.openrndr.color.ColorRGBa
import org.openrndr.math.Vector2 import org.openrndr.math.Vector2
import org.openrndr.shape.CompositionDrawer import org.openrndr.shape.CompositionDrawer
import java.io.File import java.io.File
import com.martianwabbit.saveToHPGL
import org.openrndr.Configuration
import org.openrndr.Program
fun main() = application { fun main() = application {
configure { configure {
@ -18,10 +16,12 @@ fun main() = application {
program { program {
extend { extend {
val c = CompositionDrawer() val c = CompositionDrawer()
c.circle(Vector2(20.0, 20.0), 12.0) c.selectPen(1)
c.circle(Vector2(40.0, 40.0), 30.0)
c.composition.saveToHPGL(File("output.plt")) 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() this.application.exit()
drawer.background(ColorRGBa.WHITE) drawer.background(ColorRGBa.WHITE)

View File

@ -3,44 +3,71 @@ package com.martianwabbit
import org.openrndr.math.Vector2 import org.openrndr.math.Vector2
import org.openrndr.shape.* import org.openrndr.shape.*
import java.io.File 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, width: Int = 0, height: Int = 0) {
val plot = HPGL(width = width, height = height).generateFromComposition(this)
fun Composition.saveToHPGL(file: File) { file.writeText(plot)
HPGL().generateFromComposition(this)
} }
class HPGL(pageSize: PageSize = PageSize.A4) { class HPGL(
fun generateFromComposition(composition: Composition) { private val p1: Vector2 = Vector2.ZERO,
composition.root.traverse { shape -> private val p2: Vector2 = Vector2.ZERO,
if (shape == Stage.Before) { private val width: Int = 0,
if (this is ShapeNode) { private val height: Int = 0
this.shape.contours.forEach { shape -> ) {
val i = shape.sampleEquidistant(200) fun generateFromComposition(composition: Composition): String {
val rr = StringBuilder() val hpgl = StringBuilder()
for (v in i.segments) {
rr.appendln("${v.start.x}, ${v.start.y}") 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 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
} }
calculateSpline(shape.segments)
} }
is SelectPen -> hpgl.cmd("SP", this.penNumber)
is VelocitySelect -> hpgl.cmd("VS", this.velocity)
} }
} }
} }
hpgl.cmd("PA", Vector2.ZERO)
return hpgl.toString()
} }
} }
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 { private enum class Stage {
Before, Before,
After After
@ -49,51 +76,37 @@ private enum class Stage {
private fun CompositionNode.traverse(cb: CompositionNode.(stage: Stage) -> Unit) { private fun CompositionNode.traverse(cb: CompositionNode.(stage: Stage) -> Unit) {
this.cb(Stage.Before) this.cb(Stage.Before)
if (this is GroupNode) { if (this is GroupNode && this.children.size > 0) {
this.children.forEach { it.traverse(cb) } this.children.forEach { it.traverse(cb) }
} }
this.cb(Stage.After)
} }
// These default values correspond to a centripetal Catmull-Rom spline private val Vector2.coords: String
private fun calculateSpline(segments: List<Segment>, resolution: Int = 100, alpha: Double = .5, tension: Double = .0) { get() {
val result = mutableListOf<Vector2>() return "$x,$y"
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) { // HPGL Custom Instructions
rr.appendln("${v.x}, ${v.y}") private class SelectPen(val penNumber: Int) : GroupNode()
}
val a = Vector2(0.0, 10.0).distanceTo(Vector2(13.0, 12.0)) fun CompositionDrawer.selectPen(pen: Int) {
logger.info("done") require(pen >= 0) { "The pen can only be 0 or greater." }
this.root.children.add(SelectPen(pen))
} }
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 })