Add optional QR-code overlay for orx-rabbit-control

* Optional QR-code overlay for orx-rabbit-control
* Animate the QR-code overlay
* Changed orx-rabbit-control to the new demo structure
* Changed orx-rabbit-control README.MD
This commit is contained in:
Rein van der Woerd
2020-04-07 17:04:35 +02:00
committed by GitHub
parent b98d1b7369
commit 5d3eadd156
6 changed files with 177 additions and 11 deletions

View File

@@ -6,6 +6,10 @@
<img src="https://rabbitcontrol.github.io/carrot-sketch-c-trans.png" width="50px">
</a>
For examples, check out the [demo](./src/demo/kotlin) folder.
### Using the web client
Go to [rabbitcontrol.github.io/client](https://rabbitcontrol.github.io/client/) and enter your IP-address and port.

View File

@@ -1,9 +1,27 @@
repositories {
maven { url 'https://jitpack.io' }
}
sourceSets {
demo {
java {
srcDirs = ["src/demo/kotlin"]
compileClasspath += main.getCompileClasspath()
runtimeClasspath += main.getRuntimeClasspath()
}
}
}
dependencies {
api project(":orx-parameters")
api project(":orx-compositor")
api project(":orx-image-fit")
implementation "org.openrndr:openrndr-core:$openrndrVersion"
implementation "org.openrndr:openrndr-gl3:$openrndrVersion"
implementation "org.lwjgl:lwjgl-opengl:3.2.3"
implementation "org.openrndr:openrndr-gl3-natives-macos:$openrndrVersion"
implementation "io.github.rabbitcontrol:rcp:0.3.0"
implementation "com.google.zxing:core:3.3.0"
implementation "com.google.zxing:javase:3.3.0"
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion")
demoImplementation(sourceSets.getByName("main").output)
}

View File

@@ -9,13 +9,13 @@ import org.openrndr.math.Vector4
fun main() = application {
configure {
width = 400
height = 400
width = 800
height = 800
}
program {
val rabbit = RabbitControlServer()
val font= loadFont("orx-rabbit-control/src/test/resources/fonts/Roboto-Regular.ttf", 20.0)
val font= loadFont("orx-rabbit-control/src/demo/resources/fonts/Roboto-Regular.ttf", 20.0)
val settings = object {
@TextParameter("A string")
var s: String = "Hello"

View File

@@ -0,0 +1,45 @@
import org.openrndr.KEY_HOME
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.parameters.*
fun main() = application {
configure {
width = 800
height = 800
}
program {
val rabbit = RabbitControlServer(showQRUntilClientConnects = false)
val settings = object {
@BooleanParameter("White on black")
var whiteOnBlack: Boolean = true
}
rabbit.add(settings)
extend(rabbit)
/**
* Example: only show the QR code when the [KEY_HOME] button is pressed
*/
keyboard.keyDown.listen {
when (it.key) {
KEY_HOME -> rabbit.showQRCode = true
}
}
keyboard.keyUp.listen {
when (it.key) {
KEY_HOME -> rabbit.showQRCode = false
}
}
extend {
drawer.background(if (settings.whiteOnBlack) ColorRGBa.BLACK else ColorRGBa.WHITE)
drawer.fill = if (settings.whiteOnBlack) ColorRGBa.WHITE else ColorRGBa.BLACK
drawer.circle(drawer.bounds.center, 250.0)
}
}
}

View File

@@ -1,30 +1,78 @@
import com.google.zxing.BarcodeFormat
import com.google.zxing.client.j2se.MatrixToImageWriter
import com.google.zxing.qrcode.QRCodeWriter
import org.openrndr.Extension
import org.openrndr.Program
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ColorBufferProxy
import org.openrndr.draw.Drawer
import org.openrndr.draw.isolated
import org.openrndr.extra.compositor.*
import org.openrndr.extra.fx.blend.Darken
import org.openrndr.extra.parameters.Parameter
import org.openrndr.extra.parameters.ParameterType
import org.openrndr.extra.parameters.listParameters
import org.openrndr.extra.parameters.title
import org.openrndr.extras.imageFit.FitMethod
import org.openrndr.extras.imageFit.imageFit
import org.openrndr.internal.colorBufferLoader
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
import org.openrndr.math.mix
import org.rabbitcontrol.rcp.RCPServer
import org.rabbitcontrol.rcp.model.interfaces.IParameter
import org.rabbitcontrol.rcp.model.parameter.*
import org.rabbitcontrol.rcp.transport.websocket.server.WebsocketServerTransporterNetty
import java.awt.Color
import java.io.File
import java.net.InetSocketAddress
import java.net.Socket
import java.nio.file.FileSystems
import java.nio.file.Path
import kotlin.reflect.KMutableProperty1
class RabbitControlServer : Extension {
class RabbitControlServer(private val showQRUntilClientConnects: Boolean = true, port: Int = 10000) : Extension {
private val rabbitServer = RCPServer()
private val transporter = WebsocketServerTransporterNetty()
private var parameterMap = mutableMapOf<IParameter, Pair<Any, Parameter>>()
private var qrCodeImageProxy: ColorBufferProxy? = null
private var qrImagePath: Path? = null
private var qrOverlayComposition: Composite? = null
/**
* Animate the opacity to make it look smooooth
*/
private var currentOpacity = 0.0
private var targetOpacity: Double = 0.0
get() = if (shouldShowQR) 0.8 else 0.0
private var shouldShowQR = false
get() = (rabbitServer.connectionCount == 0 && showQRUntilClientConnects) || showQRCode
/**
* Used to manually show and hide the QR code and override the default
* behaviour of (only) showing the code when no clients are connected
*/
var showQRCode = false
init {
rabbitServer.addTransporter(transporter)
transporter.bind(10000)
transporter.bind(port)
// FIXME please help me find a better way to get the local address
val socket = Socket()
socket.connect(InetSocketAddress("google.com", 80))
val ip = socket.localAddress.toString().replace("/", "")
val clientUrlWithHash = "https://rabbitcontrol.github.io/client/#$ip:$port"
qrCodeImageProxy = getQRCodeImageProxy(barcodeText = clientUrlWithHash)
println("RabbitControl Web Client: $clientUrlWithHash")
/**
* Update the object when it has been updated in RabbitControl
@@ -70,7 +118,38 @@ class RabbitControlServer : Extension {
}
fun add(objectWithParameters: Any, label: String? = objectWithParameters.title()) {
override fun setup(program: Program) {
/**
* Creating the Composite for the overlay needs to happen in setup(),
* as we need access to [Program.drawer]
*/
qrOverlayComposition = compose {
layer {
draw {
program.drawer.isolated {
fill = ColorRGBa.WHITE.opacify(currentOpacity)
stroke = null
rectangle(0.0,0.0, width.toDouble(), height.toDouble())
}
}
layer {
blend(Darken()) {
clip = true
}
draw {
qrCodeImageProxy!!.colorBuffer?.let {
program.drawer.imageFit(it, program.width / 4.0,program.height / 4.0, program.width * .5, program.height * .5, 0.0,0.0, FitMethod.Contain)
}
}
}
}
}
}
fun add(objectWithParameters: Any) {
val parameters = objectWithParameters.listParameters()
parameters.forEach {
@@ -142,6 +221,26 @@ class RabbitControlServer : Extension {
override fun shutdown(program: Program) {
transporter.dispose()
// Delete the temporary file
File(qrImagePath!!.toUri()).delete()
}
// FIXME is it possible to avoid the file entirely?
private fun getQRCodeImageProxy(barcodeText: String): ColorBufferProxy {
val qrCodeWriter = QRCodeWriter()
val bitMatrix = qrCodeWriter.encode(barcodeText, BarcodeFormat.QR_CODE, 500, 500)
qrImagePath = FileSystems.getDefault().getPath("./qr.JPG")
MatrixToImageWriter.writeToPath(bitMatrix, "JPG", qrImagePath)
return colorBufferLoader.loadFromUrl(qrImagePath!!.toUri().toURL().toString())
}
override fun afterDraw(drawer: Drawer, program: Program) {
currentOpacity = mix(targetOpacity, currentOpacity, 0.8)
// Don't draw if it isn't necessary
if (currentOpacity > 0.0) {
qrOverlayComposition?.draw(drawer)
}
}
}