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.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) | ||||||
|  |  | ||||||
|  | @ -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 }) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue