Working server prototype
This commit is contained in:
parent
a7136d3eff
commit
faddb8a393
@ -3,17 +3,19 @@ package ru.mipt.npm.sat
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.vision.VisionManager
|
||||
import hep.dataforge.vision.get
|
||||
import hep.dataforge.vision.server.visionModule
|
||||
import hep.dataforge.vision.server.close
|
||||
import hep.dataforge.vision.server.serve
|
||||
import hep.dataforge.vision.server.show
|
||||
import hep.dataforge.vision.solid.Solid
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import hep.dataforge.vision.solid.color
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.h1
|
||||
import kotlinx.html.script
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -23,28 +25,36 @@ fun main() {
|
||||
ySegments = 3,
|
||||
)
|
||||
|
||||
val context = Global.context("SAT"){
|
||||
val context = Global.context("SAT") {
|
||||
plugin(SolidManager)
|
||||
}
|
||||
|
||||
embeddedServer(CIO, 8080, host = "localhost"){
|
||||
visionModule(context).apply {
|
||||
val server = context.plugins.fetch(VisionManager).serve {
|
||||
header {
|
||||
script {
|
||||
src = "sat-demo.js"
|
||||
}
|
||||
}
|
||||
page {
|
||||
h1 { +"Satellite detector demo" }
|
||||
vision("main".asName(), sat)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
while (isActive){
|
||||
while (isActive) {
|
||||
val currentLayer = Random.nextInt(10)
|
||||
(sat["layer[$currentLayer]"] as? Solid)?.color(123)
|
||||
delay(300)
|
||||
(sat["layer[$currentLayer]"] as? Solid)?.color = null
|
||||
}
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
||||
server.show()
|
||||
|
||||
println("Press Enter to close server")
|
||||
while (readLine()!="exit"){
|
||||
//
|
||||
}
|
||||
|
||||
server.close()
|
||||
|
||||
}
|
@ -20,7 +20,7 @@ import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
|
||||
class ThreeDemoGrid(element: Element, idPrefix: String = "") : Page<Solid> {
|
||||
class ThreeDemoGrid(element: Element) : Page<Solid> {
|
||||
private lateinit var navigationElement: HTMLElement
|
||||
private lateinit var contentElement: HTMLDivElement
|
||||
|
||||
|
@ -5,23 +5,23 @@ import hep.dataforge.vision.Vision
|
||||
import kotlinx.html.FlowContent
|
||||
import kotlinx.html.TagConsumer
|
||||
|
||||
public class BindingHtmlOutputScope<T, V : Vision>(
|
||||
public class BindingOutputTagConsumer<T, V : Vision>(
|
||||
root: TagConsumer<T>,
|
||||
prefix: String? = null,
|
||||
) : HtmlOutputScope<T, V>(root, prefix) {
|
||||
) : OutputTagConsumer<T, V>(root, prefix) {
|
||||
|
||||
private val _bindings = HashMap<Name, V>()
|
||||
public val bindings: Map<Name, V> get() = _bindings
|
||||
|
||||
override fun renderVision(htmlOutput: HtmlOutput<V>, vision: V) {
|
||||
_bindings[htmlOutput.name] = vision
|
||||
override fun FlowContent.renderVision(name: Name, vision: V) {
|
||||
_bindings[name] = vision
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T : Any> TagConsumer<T>.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||
return BindingHtmlOutputScope<T, Vision>(this).apply(fragment.content).bindings
|
||||
return BindingOutputTagConsumer<T, Vision>(this).apply(fragment.content).bindings
|
||||
}
|
||||
|
||||
public fun FlowContent.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||
return BindingHtmlOutputScope<Any?, Vision>(consumer).apply(fragment.content).bindings
|
||||
return BindingOutputTagConsumer<Any?, Vision>(consumer).apply(fragment.content).bindings
|
||||
}
|
@ -14,7 +14,7 @@ public fun FlowContent.fragment(fragment: HtmlFragment) {
|
||||
fragment.content(consumer)
|
||||
}
|
||||
|
||||
public class HtmlVisionFragment<V : Vision>(public val content: HtmlOutputScope<*, V>.() -> Unit)
|
||||
public class HtmlVisionFragment<V : Vision>(public val content: OutputTagConsumer<*, V>.() -> Unit)
|
||||
|
||||
public fun buildVisionFragment(block: HtmlOutputScope<*, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
||||
public fun buildVisionFragment(block: OutputTagConsumer<*, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
||||
HtmlVisionFragment(block)
|
||||
|
@ -5,47 +5,52 @@ import hep.dataforge.names.toName
|
||||
import hep.dataforge.vision.Vision
|
||||
import kotlinx.html.*
|
||||
|
||||
public class HtmlOutput<V : Vision>(
|
||||
public val outputScope: HtmlOutputScope<*, V>,
|
||||
/**
|
||||
* An HTML div wrapper that includes the output [name] and inherited [render] function
|
||||
*/
|
||||
public class OutputDiv<in V : Vision>(
|
||||
private val div: DIV,
|
||||
public val name: Name,
|
||||
public val div: DIV,
|
||||
)
|
||||
public val render: (V) -> Unit,
|
||||
) : HtmlBlockTag by div
|
||||
|
||||
public abstract class HtmlOutputScope<R, V : Vision>(
|
||||
/**
|
||||
* Modified [TagConsumer] that allows rendering output fragments and visions in them
|
||||
*/
|
||||
public abstract class OutputTagConsumer<R, V : Vision>(
|
||||
private val root: TagConsumer<R>,
|
||||
public val idPrefix: String? = null,
|
||||
private val idPrefix: String? = null,
|
||||
) : TagConsumer<R> by root {
|
||||
|
||||
public open fun resolveId(name: Name): String = (idPrefix ?: "output:") + name.toString()
|
||||
|
||||
/**
|
||||
* Create a placeholder but do not attach any [Vision] to it
|
||||
* Render a vision inside the output fragment
|
||||
*/
|
||||
public inline fun <T> TagConsumer<T>.visionOutput(
|
||||
protected abstract fun FlowContent.renderVision(name: Name, vision: V)
|
||||
|
||||
/**
|
||||
* Create a placeholder for an output window
|
||||
*/
|
||||
public fun <T> TagConsumer<T>.visionOutput(
|
||||
name: Name,
|
||||
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
||||
block: OutputDiv<V>.() -> Unit = {},
|
||||
): T = div {
|
||||
id = resolveId(name)
|
||||
classes = setOf(OUTPUT_CLASS)
|
||||
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
HtmlOutput(this@HtmlOutputScope, name, this).block()
|
||||
OutputDiv<V>(this, name) { renderVision(name, it) }.block()
|
||||
}
|
||||
|
||||
|
||||
public inline fun <T> TagConsumer<T>.visionOutput(
|
||||
public fun <T> TagConsumer<T>.visionOutput(
|
||||
name: String,
|
||||
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
||||
block: OutputDiv<V>.() -> Unit = {},
|
||||
): T = visionOutput(name.toName(), block)
|
||||
|
||||
/**
|
||||
* Create a placeholder and put a [Vision] in it
|
||||
*/
|
||||
public abstract fun renderVision(htmlOutput: HtmlOutput<V>, vision: V)
|
||||
|
||||
public fun <T> TagConsumer<T>.vision(name: Name, vision: V): Unit {
|
||||
visionOutput(name) {
|
||||
renderVision(this, vision)
|
||||
render(vision)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package hep.dataforge.vision.html
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.vision.Vision
|
||||
import kotlinx.html.FlowContent
|
||||
import kotlinx.html.TagConsumer
|
||||
@ -7,22 +8,23 @@ import kotlinx.html.stream.createHTML
|
||||
|
||||
public typealias HtmlVisionRenderer<V> = FlowContent.(V) -> Unit
|
||||
|
||||
public class StaticHtmlOutputScope<R, V : Vision>(
|
||||
/**
|
||||
* An [OutputTagConsumer] that directly renders given [Vision] using provided [renderer]
|
||||
*/
|
||||
public class StaticOutputTagConsumer<R, V : Vision>(
|
||||
root: TagConsumer<R>,
|
||||
prefix: String? = null,
|
||||
private val render: HtmlVisionRenderer<V>,
|
||||
) : HtmlOutputScope<R, V>(root, prefix) {
|
||||
private val renderer: HtmlVisionRenderer<V>,
|
||||
) : OutputTagConsumer<R, V>(root, prefix) {
|
||||
|
||||
override fun renderVision(htmlOutput: HtmlOutput<V>, vision: V) {
|
||||
htmlOutput.div.render(vision)
|
||||
}
|
||||
override fun FlowContent.renderVision(name: Name, vision: V): Unit = renderer(vision)
|
||||
}
|
||||
|
||||
public fun <T : Any> HtmlVisionFragment<Vision>.renderToObject(
|
||||
root: TagConsumer<T>,
|
||||
prefix: String? = null,
|
||||
renderer: HtmlVisionRenderer<Vision>,
|
||||
): T = StaticHtmlOutputScope(root, prefix, renderer).apply(content).finalize()
|
||||
): T = StaticOutputTagConsumer(root, prefix, renderer).apply(content).finalize()
|
||||
|
||||
public fun HtmlVisionFragment<Vision>.renderToString(renderer: HtmlVisionRenderer<Vision>): String =
|
||||
renderToObject(createHTML(), null, renderer)
|
@ -10,16 +10,17 @@ import kotlin.test.Test
|
||||
|
||||
class HtmlTagTest {
|
||||
|
||||
fun HtmlOutput<Vision>.vision(block: Vision.() -> Unit) =
|
||||
outputScope.renderVision(this, VisionBase().apply(block))
|
||||
fun OutputDiv<Vision>.visionBase(block: VisionBase.() -> Unit) =
|
||||
render(VisionBase().apply(block))
|
||||
|
||||
val fragment = buildVisionFragment {
|
||||
div {
|
||||
h1 { +"Head" }
|
||||
visionOutput("ddd") {
|
||||
vision {
|
||||
visionBase {
|
||||
configure {
|
||||
set("myProp", 82)
|
||||
set("otherProp", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ package hep.dataforge.vision.client
|
||||
|
||||
import hep.dataforge.context.*
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionChange
|
||||
import hep.dataforge.vision.VisionManager
|
||||
import hep.dataforge.vision.html.HtmlOutputScope
|
||||
import hep.dataforge.vision.html.HtmlOutputScope.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||
import hep.dataforge.vision.html.HtmlOutputScope.Companion.OUTPUT_NAME_ATTRIBUTE
|
||||
import hep.dataforge.vision.html.OutputTagConsumer
|
||||
import hep.dataforge.vision.html.OutputTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||
import hep.dataforge.vision.html.OutputTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.browser.window
|
||||
import org.w3c.dom.Element
|
||||
@ -41,12 +43,12 @@ public class VisionClient : AbstractPlugin() {
|
||||
getRenderers().maxByOrNull { it.rateVision(vision) }
|
||||
|
||||
/**
|
||||
* Fetch from server and render a vision, described in a given with [HtmlOutputScope.OUTPUT_CLASS] class.
|
||||
* Fetch from server and render a vision, described in a given with [OutputTagConsumer.OUTPUT_CLASS] class.
|
||||
*/
|
||||
public fun fetchAndRenderVision(element: Element, requestUpdates: Boolean = true) {
|
||||
val name = resolveName(element) ?: error("The element is not a vision output")
|
||||
console.info("Found DF output with name $name")
|
||||
if (!element.classList.contains(HtmlOutputScope.OUTPUT_CLASS)) error("The element $element is not an output element")
|
||||
if (!element.classList.contains(OutputTagConsumer.OUTPUT_CLASS)) error("The element $element is not an output element")
|
||||
val endpoint = resolveEndpoint(element)
|
||||
console.info("Vision server is resolved to $endpoint")
|
||||
|
||||
@ -60,9 +62,9 @@ public class VisionClient : AbstractPlugin() {
|
||||
if (response.ok) {
|
||||
response.text().then { text ->
|
||||
val vision = visionManager.decodeFromString(text)
|
||||
|
||||
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
||||
renderer.render(element, vision)
|
||||
val rendererConfiguration = vision.properties[RENDERER_CONFIGURATION_META_KEY].node ?: Meta.EMPTY
|
||||
renderer.render(element, vision, rendererConfiguration)
|
||||
if (requestUpdates) {
|
||||
val wsUrl = URL(endpoint).apply {
|
||||
pathname += "/ws"
|
||||
@ -73,9 +75,10 @@ public class VisionClient : AbstractPlugin() {
|
||||
onmessage = { messageEvent ->
|
||||
val stringData: String? = messageEvent.data as? String
|
||||
if (stringData != null) {
|
||||
// console.info("Received WS update: $stringData")
|
||||
val dif = visionManager.jsonFormat
|
||||
.decodeFromString(VisionChange.serializer(), stringData)
|
||||
val dif = visionManager.jsonFormat.decodeFromString(
|
||||
VisionChange.serializer(),
|
||||
stringData
|
||||
)
|
||||
vision.update(dif)
|
||||
} else {
|
||||
console.error("WebSocket message data is not a string")
|
||||
@ -103,6 +106,8 @@ public class VisionClient : AbstractPlugin() {
|
||||
|
||||
public companion object : PluginFactory<VisionClient> {
|
||||
|
||||
public const val RENDERER_CONFIGURATION_META_KEY: String = "@renderer"
|
||||
|
||||
override fun invoke(meta: Meta, context: Context): VisionClient = VisionClient()
|
||||
|
||||
override val tag: PluginTag = PluginTag(name = "vision.client", group = PluginTag.DATAFORGE_GROUP)
|
||||
@ -112,10 +117,10 @@ public class VisionClient : AbstractPlugin() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and render visions for all elements with [HtmlOutputScope.OUTPUT_CLASS] class inside given [element].
|
||||
* Fetch and render visions for all elements with [OutputTagConsumer.OUTPUT_CLASS] class inside given [element].
|
||||
*/
|
||||
public fun VisionClient.fetchVisionsInChildren(element: Element, requestUpdates: Boolean = true) {
|
||||
val elements = element.getElementsByClassName(HtmlOutputScope.OUTPUT_CLASS)
|
||||
val elements = element.getElementsByClassName(OutputTagConsumer.OUTPUT_CLASS)
|
||||
console.info("Finished search for outputs. Found ${elements.length} items")
|
||||
elements.asList().forEach { child ->
|
||||
fetchAndRenderVision(child, requestUpdates)
|
||||
@ -123,7 +128,7 @@ public fun VisionClient.fetchVisionsInChildren(element: Element, requestUpdates:
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch visions from the server for all elements with [HtmlOutputScope.OUTPUT_CLASS] class in the document body
|
||||
* Fetch visions from the server for all elements with [OutputTagConsumer.OUTPUT_CLASS] class in the document body
|
||||
*/
|
||||
public fun VisionClient.fetchAndRenderAllVisions(requestUpdates: Boolean = true) {
|
||||
val element = document.body ?: error("Document does not have a body")
|
||||
|
@ -1,13 +1,14 @@
|
||||
package hep.dataforge.vision.client
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.html.BindingHtmlOutputScope
|
||||
import hep.dataforge.vision.html.HtmlOutputScope
|
||||
import hep.dataforge.vision.html.BindingOutputTagConsumer
|
||||
import hep.dataforge.vision.html.HtmlVisionFragment
|
||||
import hep.dataforge.vision.html.OutputTagConsumer
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.html.TagConsumer
|
||||
import org.w3c.dom.*
|
||||
@ -25,7 +26,7 @@ public interface ElementVisionRenderer {
|
||||
/**
|
||||
* Display the [vision] inside a given [element] replacing its current content
|
||||
*/
|
||||
public fun render(element: Element, vision: Vision): Unit
|
||||
public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY): Unit
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "elementVisionRenderer"
|
||||
@ -44,11 +45,11 @@ public fun Map<String, Vision>.bind(rendererFactory: (Vision) -> ElementVisionRe
|
||||
|
||||
@DFExperimental
|
||||
public fun Element.renderAllVisions(visionProvider: (Name) -> Vision, rendererFactory: (Vision) -> ElementVisionRenderer) {
|
||||
val elements = getElementsByClassName(HtmlOutputScope.OUTPUT_CLASS)
|
||||
val elements = getElementsByClassName(OutputTagConsumer.OUTPUT_CLASS)
|
||||
elements.asList().forEach { element ->
|
||||
val name = element.attributes[HtmlOutputScope.OUTPUT_NAME_ATTRIBUTE]?.value
|
||||
val name = element.attributes[OutputTagConsumer.OUTPUT_NAME_ATTRIBUTE]?.value
|
||||
if (name == null) {
|
||||
console.error("Attribute ${HtmlOutputScope.OUTPUT_NAME_ATTRIBUTE} not defined in the output element")
|
||||
console.error("Attribute ${OutputTagConsumer.OUTPUT_NAME_ATTRIBUTE} not defined in the output element")
|
||||
return@forEach
|
||||
}
|
||||
val vision = visionProvider(name.toName())
|
||||
@ -65,7 +66,7 @@ public fun Document.renderAllVisions(visionProvider: (Name) -> Vision, rendererF
|
||||
public fun HtmlVisionFragment<Vision>.renderInDocument(
|
||||
root: TagConsumer<HTMLElement>,
|
||||
renderer: ElementVisionRenderer,
|
||||
): HTMLElement = BindingHtmlOutputScope<HTMLElement, Vision>(root).apply(content).let { scope ->
|
||||
): HTMLElement = BindingOutputTagConsumer<HTMLElement, Vision>(root).apply(content).let { scope ->
|
||||
scope.finalize().apply {
|
||||
scope.bindings.forEach { (name, vision) ->
|
||||
val id = scope.resolveId(name)
|
||||
|
@ -346,7 +346,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) {
|
||||
final.prototypes {
|
||||
proto.children.forEach { (token, item) ->
|
||||
item.parent = null
|
||||
set(token.asName(), item)
|
||||
set(token.asName(), item as? Solid)
|
||||
}
|
||||
}
|
||||
styleCache.forEach {
|
||||
|
@ -29,10 +29,14 @@ import io.ktor.routing.application
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.routing.routing
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import io.ktor.util.error
|
||||
import io.ktor.websocket.WebSockets
|
||||
import io.ktor.websocket.webSocket
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.stream.createHTML
|
||||
@ -47,7 +51,7 @@ public class VisionServer internal constructor(
|
||||
private val visionManager: VisionManager,
|
||||
private val application: Application,
|
||||
private val rootRoute: String,
|
||||
) : Configurable {
|
||||
) : Configurable, CoroutineScope by application {
|
||||
override val config: Config = Config()
|
||||
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
public var cacheFragments: Boolean by config.boolean(true)
|
||||
@ -78,23 +82,8 @@ public class VisionServer internal constructor(
|
||||
title(title)
|
||||
}
|
||||
body {
|
||||
// attributes[OUTPUT_ENDPOINT_ATTRIBUTE] = if (rootRoute.endsWith("/")) {
|
||||
// rootRoute
|
||||
// } else {
|
||||
// "$rootRoute/"
|
||||
// }
|
||||
//Load the fragment and remember all loaded visions
|
||||
visionMap = visionFragment(visionFragment)
|
||||
// //The script runs when all headers already with required libraries are already loaded
|
||||
// script {
|
||||
// type = "text/javascript"
|
||||
//
|
||||
// val normalizedRoute =
|
||||
// unsafe {
|
||||
// //language=JavaScript
|
||||
// +"fetchAndRenderAllVisions()"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return visionMap
|
||||
@ -171,7 +160,7 @@ public class VisionServer internal constructor(
|
||||
route: String = DEFAULT_PAGE,
|
||||
title: String = "VisionForge server page '$route'",
|
||||
headers: List<HtmlFragment> = emptyList(),
|
||||
content: HtmlOutputScope<*, Vision>.() -> Unit,
|
||||
content: OutputTagConsumer<*, Vision>.() -> Unit,
|
||||
) {
|
||||
page(buildVisionFragment(content), route, title, headers)
|
||||
}
|
||||
@ -215,6 +204,15 @@ public fun Application.visionModule(context: Context, route: String = DEFAULT_PA
|
||||
return VisionServer(visionManager, this, route)
|
||||
}
|
||||
|
||||
@OptIn(KtorExperimentalAPI::class)
|
||||
public fun VisionManager.serve(
|
||||
host: String = "localhost",
|
||||
port: Int = 7777,
|
||||
block: VisionServer.()->Unit
|
||||
): ApplicationEngine = context.embeddedServer(CIO, port, host) {
|
||||
visionModule(context).apply(block)
|
||||
}.start()
|
||||
|
||||
public fun ApplicationEngine.show() {
|
||||
val connector = environment.connectors.first()
|
||||
val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
||||
|
@ -65,9 +65,9 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
serializersModule = serializersModuleForSolids
|
||||
}
|
||||
|
||||
internal fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid)
|
||||
public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid)
|
||||
|
||||
internal fun decodeFromString(str: String): Vision = jsonForSolids.decodeFromString(PolymorphicSerializer(Vision::class), str).also {
|
||||
fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str).also {
|
||||
if(it is VisionGroup){
|
||||
it.attachChildren()
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
|
||||
}
|
||||
|
||||
override fun render(element: Element, vision: Vision) {
|
||||
createCanvas(element).render(vision as? Solid ?: error("Only solids are rendered"))
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
createCanvas(element, Canvas3DOptions.read(meta)).render(vision as? Solid ?: error("Only solids are rendered"))
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<ThreePlugin> {
|
||||
|
Loading…
Reference in New Issue
Block a user