diff --git a/orx-rabbit-control/README.MD b/orx-rabbit-control/README.MD new file mode 100644 index 00000000..6002e42f --- /dev/null +++ b/orx-rabbit-control/README.MD @@ -0,0 +1,13 @@ +# orx-rabbit-control + +`orx-rabbit-control` uses `orx-parameters` annotations to generate a control interface, just like `orx-gui`. It can be used as an alternative to `orx-gui` when you want to control your OPENRNDR program from a mobile device or a different computer. + + + + + +### Using the web client + +Go to [rabbitcontrol.github.io/client](https://rabbitcontrol.github.io/client/) and enter your IP-address and port. + +_Note: there are currently issues with the insecure websocket connection that RabbitControl uses and some browsers (like mobile Safari), we're looking for solutions..._ diff --git a/orx-rabbit-control/build.gradle b/orx-rabbit-control/build.gradle new file mode 100644 index 00000000..2080ff96 --- /dev/null +++ b/orx-rabbit-control/build.gradle @@ -0,0 +1,9 @@ +dependencies { + api project(":orx-parameters") + + 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" +} diff --git a/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt b/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt new file mode 100644 index 00000000..f41130be --- /dev/null +++ b/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt @@ -0,0 +1,150 @@ +import org.openrndr.Extension +import org.openrndr.Program +import org.openrndr.color.ColorRGBa +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.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.math.Vector4 +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 kotlin.reflect.KMutableProperty1 + + +class RabbitControlServer : Extension { + private val rabbitServer = RCPServer() + private val transporter = WebsocketServerTransporterNetty() + + private var parameterMap = mutableMapOf>() + + init { + rabbitServer.addTransporter(transporter) + transporter.bind(10000) + + /** + * Update the object when it has been updated in RabbitControl + */ + rabbitServer.setUpdateListener { + val (obj, orxParameter) = parameterMap[it]!! + when(it) { + is Int32Parameter -> { + val v = it.value + orxParameter.property.qset(obj, v) + } + is Float64Parameter -> { + val v = it.value + orxParameter.property.qset(obj, v) + } + is BooleanParameter -> { + val v = it.value + orxParameter.property.qset(obj, v) + } + is StringParameter -> { + val v = it.value + orxParameter.property.qset(obj, v) + } + is RGBAParameter -> { + val c = it.value + val cc = ColorRGBa(c.red.toDouble() / 255.0, c.green.toDouble() / 255.0, c.blue.toDouble() / 255.0, c.alpha.toDouble() / 255.0) + orxParameter.property.qset(obj, cc) + } + is Vector2Float32Parameter -> { + val v = it.value + orxParameter.property.qset(obj, Vector2(v.x.toDouble(), v.y.toDouble())) + } + is Vector3Float32Parameter -> { + val v = it.value + orxParameter.property.qset(obj, Vector3(v.x.toDouble(), v.y.toDouble(), v.z.toDouble())) + } + is Vector4Float32Parameter -> { + val v = it.value + orxParameter.property.qset(obj, Vector4(v.x.toDouble(), v.y.toDouble(), v.z.toDouble(), v.t.toDouble())) + } + } + } + } + + + fun add(objectWithParameters: Any, label: String? = objectWithParameters.title()) { + val parameters = objectWithParameters.listParameters() + + parameters.forEach { + val rabbitParam = when (it.parameterType) { + ParameterType.Int -> { + val param = rabbitServer.createInt32Parameter(it.label) + param.value = (it.property as KMutableProperty1).get(objectWithParameters) + param + } + ParameterType.Double -> { + val param = rabbitServer.createFloat64Parameter(it.label) + param.value = (it.property as KMutableProperty1).get(objectWithParameters) + param + } + ParameterType.Action -> { + val param = rabbitServer.createBangParameter(it.label) + param.setFunction { + it.function!!.call(objectWithParameters) + } + param + } + ParameterType.Boolean -> { + val param = rabbitServer.createBooleanParameter(it.label) + param.value = (it.property as KMutableProperty1).get(objectWithParameters) + param + } + ParameterType.Text -> { + val param =rabbitServer.createStringParameter(it.label) + param.value = (it.property as KMutableProperty1).get(objectWithParameters) + param + } + ParameterType.Color -> { + val param = rabbitServer.createRGBAParameter(it.label) + val c = (it.property as KMutableProperty1).get(objectWithParameters) + param.value = Color(c.r.toFloat(), c.g.toFloat(), c.b.toFloat(), c.a.toFloat()) + param + } + ParameterType.Vector2 -> { + val param = rabbitServer.createVector2Float32Parameter(it.label) + val v2 = (it.property as KMutableProperty1).get(objectWithParameters) + param.value = org.rabbitcontrol.rcp.model.types.Vector2(v2.x.toFloat(), v2.y.toFloat()) + param + } + ParameterType.Vector3 -> { + val param = rabbitServer.createVector3Float32Parameter(it.label) + val v3 = (it.property as KMutableProperty1).get(objectWithParameters) + param.value = org.rabbitcontrol.rcp.model.types.Vector3(v3.x.toFloat(), v3.y.toFloat(), v3.z.toFloat()) + param + } + ParameterType.Vector4 -> { + val param = rabbitServer.createVector4Float32Parameter(it.label) + val v4 = (it.property as KMutableProperty1).get(objectWithParameters) + param.value = org.rabbitcontrol.rcp.model.types.Vector4(v4.x.toFloat(), v4.y.toFloat(), v4.z.toFloat(), v4.w.toFloat()) + param + } + + else -> rabbitServer.createBangParameter(it.label) + } + + // We need to store a mapping from Rabbit parameter to target object + orx parameter + // so we can update the object later + parameterMap[rabbitParam] = Pair(objectWithParameters, it) + } + + rabbitServer.update() + } + + override var enabled = true + + override fun shutdown(program: Program) { + transporter.dispose() + } +} + +fun KMutableProperty1?.qset(obj: Any, value: T) { + return (this as KMutableProperty1).set(obj, value) +} diff --git a/orx-rabbit-control/src/test/kotlin/RabbitControlServerTest.kt b/orx-rabbit-control/src/test/kotlin/RabbitControlServerTest.kt new file mode 100644 index 00000000..797a1236 --- /dev/null +++ b/orx-rabbit-control/src/test/kotlin/RabbitControlServerTest.kt @@ -0,0 +1,62 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.loadFont +import org.openrndr.extra.parameters.* +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.math.Vector4 + + +fun main() = application { + configure { + width = 400 + height = 400 + } + + program { + val rabbit = RabbitControlServer() + val font= loadFont("orx-rabbit-control/src/test/resources/fonts/Roboto-Regular.ttf", 20.0) + val settings = object { + @TextParameter("A string") + var s: String = "Hello" + + @DoubleParameter("A double", 0.0, 10.0) + var d: Double = 10.0 + + @BooleanParameter("A bool") + var b: Boolean = true + + @ColorParameter("A fill color") + var fill = ColorRGBa.PINK + + @ColorParameter("A stroke color") + var stroke = ColorRGBa.WHITE + + @Vector2Parameter("A vector2") + var v2 = Vector2(200.0,200.0) + + @Vector3Parameter("A vector3") + var v3 = Vector3(200.0, 200.0, 200.0) + + @Vector4Parameter("A vector4") + var v4 = Vector4(200.0, 200.0, 200.0, 200.0) + + @ActionParameter("Action test") + fun clicked() { + println("Clicked from RabbitControl") + } + } + + rabbit.add(settings) + + extend(rabbit) + extend { + drawer.background(if (settings.b) ColorRGBa.BLUE else ColorRGBa.BLACK) + drawer.fontMap = font + drawer.fill = settings.fill + drawer.stroke = settings.stroke + drawer.circle(settings.v2, settings.d) + drawer.text(settings.s, 10.0, 20.0) + } + } +} diff --git a/orx-rabbit-control/src/test/resources/fonts/Roboto-Regular.ttf b/orx-rabbit-control/src/test/resources/fonts/Roboto-Regular.ttf new file mode 100644 index 00000000..8c082c8d Binary files /dev/null and b/orx-rabbit-control/src/test/resources/fonts/Roboto-Regular.ttf differ diff --git a/settings.gradle b/settings.gradle index 0235c1ee..b12d25dd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,7 @@ include 'orx-camera', 'orx-palette', 'orx-panel', 'orx-poisson-fill', + 'orx-rabbit-control', 'orx-runway', 'orx-shader-phrases', 'orx-shade-styles',