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.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)

View File

@ -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
}
}
}
}
}
}
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("PU") // Done plotting
}
}
enum class PageSize {
A4,
A3
is SelectPen -> hpgl.cmd("SP", this.penNumber)
is VelocitySelect -> hpgl.cmd("VS", this.velocity)
}
}
}
hpgl.cmd("PA", Vector2.ZERO)
return hpgl.toString()
}
}
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}")
}
val a = Vector2(0.0, 10.0).distanceTo(Vector2(13.0, 12.0))
logger.info("done")
// 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))
}
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 })