diff --git a/demo/sat-demo/src/jvmMain/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/jvmMain/kotlin/ru/mipt/npm/sat/satServer.kt index eea0a4bb..6605b17d 100644 --- a/demo/sat-demo/src/jvmMain/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/jvmMain/kotlin/ru/mipt/npm/sat/satServer.kt @@ -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 { - header { - script { - src = "sat-demo.js" - } - } - page { - vision("main".asName(), sat) + 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() + } \ No newline at end of file diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt index a53a4529..f7268a12 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt @@ -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 { +class ThreeDemoGrid(element: Element) : Page { private lateinit var navigationElement: HTMLElement private lateinit var contentElement: HTMLDivElement diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingHtmlOutputScope.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingOutputTagConsumer.kt similarity index 58% rename from visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingHtmlOutputScope.kt rename to visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingOutputTagConsumer.kt index 9ee0555c..445ffb0c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingHtmlOutputScope.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/BindingOutputTagConsumer.kt @@ -5,23 +5,23 @@ import hep.dataforge.vision.Vision import kotlinx.html.FlowContent import kotlinx.html.TagConsumer -public class BindingHtmlOutputScope( +public class BindingOutputTagConsumer( root: TagConsumer, prefix: String? = null, -) : HtmlOutputScope(root, prefix) { +) : OutputTagConsumer(root, prefix) { private val _bindings = HashMap() public val bindings: Map get() = _bindings - override fun renderVision(htmlOutput: HtmlOutput, vision: V) { - _bindings[htmlOutput.name] = vision + override fun FlowContent.renderVision(name: Name, vision: V) { + _bindings[name] = vision } } public fun TagConsumer.visionFragment(fragment: HtmlVisionFragment): Map { - return BindingHtmlOutputScope(this).apply(fragment.content).bindings + return BindingOutputTagConsumer(this).apply(fragment.content).bindings } public fun FlowContent.visionFragment(fragment: HtmlVisionFragment): Map { - return BindingHtmlOutputScope(consumer).apply(fragment.content).bindings + return BindingOutputTagConsumer(consumer).apply(fragment.content).bindings } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlVisionFragment.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlVisionFragment.kt index bddff870..6ae41f25 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlVisionFragment.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlVisionFragment.kt @@ -14,7 +14,7 @@ public fun FlowContent.fragment(fragment: HtmlFragment) { fragment.content(consumer) } -public class HtmlVisionFragment(public val content: HtmlOutputScope<*, V>.() -> Unit) +public class HtmlVisionFragment(public val content: OutputTagConsumer<*, V>.() -> Unit) -public fun buildVisionFragment(block: HtmlOutputScope<*, Vision>.() -> Unit): HtmlVisionFragment = +public fun buildVisionFragment(block: OutputTagConsumer<*, Vision>.() -> Unit): HtmlVisionFragment = HtmlVisionFragment(block) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlOutputScope.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/OutputTagConsumer.kt similarity index 60% rename from visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlOutputScope.kt rename to visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/OutputTagConsumer.kt index 87901ca9..5e402ab4 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/HtmlOutputScope.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/OutputTagConsumer.kt @@ -5,47 +5,52 @@ import hep.dataforge.names.toName import hep.dataforge.vision.Vision import kotlinx.html.* -public class HtmlOutput( - public val outputScope: HtmlOutputScope<*, V>, +/** + * An HTML div wrapper that includes the output [name] and inherited [render] function + */ +public class OutputDiv( + private val div: DIV, public val name: Name, - public val div: DIV, -) + public val render: (V) -> Unit, +) : HtmlBlockTag by div -public abstract class HtmlOutputScope( +/** + * Modified [TagConsumer] that allows rendering output fragments and visions in them + */ +public abstract class OutputTagConsumer( private val root: TagConsumer, - public val idPrefix: String? = null, + private val idPrefix: String? = null, ) : TagConsumer 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 TagConsumer.visionOutput( + protected abstract fun FlowContent.renderVision(name: Name, vision: V) + + /** + * Create a placeholder for an output window + */ + public fun TagConsumer.visionOutput( name: Name, - crossinline block: HtmlOutput.() -> Unit = {}, + block: OutputDiv.() -> 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(this, name) { renderVision(name, it) }.block() } - - public inline fun TagConsumer.visionOutput( + public fun TagConsumer.visionOutput( name: String, - crossinline block: HtmlOutput.() -> Unit = {}, + block: OutputDiv.() -> Unit = {}, ): T = visionOutput(name.toName(), block) - /** - * Create a placeholder and put a [Vision] in it - */ - public abstract fun renderVision(htmlOutput: HtmlOutput, vision: V) public fun TagConsumer.vision(name: Name, vision: V): Unit { visionOutput(name) { - renderVision(this, vision) + render(vision) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticHtmlOutputScope.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticOutputTagConsumer.kt similarity index 53% rename from visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticHtmlOutputScope.kt rename to visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticOutputTagConsumer.kt index b07c575c..401fe3f8 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticHtmlOutputScope.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/StaticOutputTagConsumer.kt @@ -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 = FlowContent.(V) -> Unit -public class StaticHtmlOutputScope( +/** + * An [OutputTagConsumer] that directly renders given [Vision] using provided [renderer] + */ +public class StaticOutputTagConsumer( root: TagConsumer, prefix: String? = null, - private val render: HtmlVisionRenderer, -) : HtmlOutputScope(root, prefix) { + private val renderer: HtmlVisionRenderer, +) : OutputTagConsumer(root, prefix) { - override fun renderVision(htmlOutput: HtmlOutput, vision: V) { - htmlOutput.div.render(vision) - } + override fun FlowContent.renderVision(name: Name, vision: V): Unit = renderer(vision) } public fun HtmlVisionFragment.renderToObject( root: TagConsumer, prefix: String? = null, renderer: HtmlVisionRenderer, -): T = StaticHtmlOutputScope(root, prefix, renderer).apply(content).finalize() +): T = StaticOutputTagConsumer(root, prefix, renderer).apply(content).finalize() public fun HtmlVisionFragment.renderToString(renderer: HtmlVisionRenderer): String = renderToObject(createHTML(), null, renderer) \ No newline at end of file diff --git a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt index 4c48fad7..2b322c12 100644 --- a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt @@ -10,16 +10,17 @@ import kotlin.test.Test class HtmlTagTest { - fun HtmlOutput.vision(block: Vision.() -> Unit) = - outputScope.renderVision(this, VisionBase().apply(block)) + fun OutputDiv.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) } } } diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index df61586f..ad022eee 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -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 { + 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") diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/elementOutput.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/elementOutput.kt index 3673ecc2..745754dc 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/elementOutput.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/elementOutput.kt @@ -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.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.renderInDocument( root: TagConsumer, renderer: ElementVisionRenderer, -): HTMLElement = BindingHtmlOutputScope(root).apply(content).let { scope -> +): HTMLElement = BindingOutputTagConsumer(root).apply(content).let { scope -> scope.finalize().apply { scope.bindings.forEach { (name, vision) -> val id = scope.resolveId(name) diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt index a0607435..583bbb03 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt @@ -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 { diff --git a/visionforge-server/src/main/kotlin/hep/dataforge/vision/server/VisionServer.kt b/visionforge-server/src/main/kotlin/hep/dataforge/vision/server/VisionServer.kt index 33b69d0c..4eaf75ed 100644 --- a/visionforge-server/src/main/kotlin/hep/dataforge/vision/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/hep/dataforge/vision/server/VisionServer.kt @@ -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 = 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) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index d098a72b..a01cc961 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -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() } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 63740483..c1926dcd 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -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 {