From 359eb05f8398f7c86b84890da9ffce23b6209a4f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 5 Jun 2021 22:31:16 +0300 Subject: [PATCH] Fix canvas update --- demo/gdml/build.gradle.kts | 1 - .../VisionForgePlayGroundForJupyter.kt | 4 +- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 9 +- .../ru/mipt/npm/muon/monitor/MMDemoApp.kt | 5 +- .../mipt/npm/muon/monitor/server/MMServer.kt | 2 +- demo/sat-demo/build.gradle.kts | 3 +- .../kscience/visionforge/solid/demo/demo.kt | 9 +- .../visionforge/solid/demo/ThreeDemoGrid.kt | 3 +- ui/material/build.gradle.kts | 15 --- .../hep/dataforge/vision/material/icons.kt | 11 --- .../hep/dataforge/vision/material/misc.kt | 97 ------------------- .../dataforge/vision/material/objectTree.kt | 94 ------------------ .../materialui/components/slider/builder.kt | 37 ------- .../materialui/components/slider/enums.kt | 18 ---- .../materialui/components/slider/slider.kt | 38 -------- .../visionforge/react/ThreeCanvasComponent.kt | 8 +- .../space/kscience/visionforge/Vision.kt | 9 +- .../space/kscience/visionforge/VisionBase.kt | 18 ++-- .../space/kscience/visionforge/VisionGroup.kt | 3 +- .../kscience/visionforge/VisionGroupBase.kt | 14 ++- .../visionforge/html/HtmlVisionRenderer.kt | 6 +- .../visionforge/html/VisionTagConsumer.kt | 7 +- .../kscience/visionforge/html/HtmlTagTest.kt | 13 ++- .../kscience/visionforge/VisionClient.kt | 2 +- .../kscience/visionforge/elementOutput.kt | 2 +- visionforge-plotly/build.gradle.kts | 2 +- .../visionforge/plotly/VisionOfPlotly.kt | 19 +++- .../kscience/visionforge/plotly/plotlyJs.kt | 2 + .../visionforge/three/server/VisionServer.kt | 39 ++++---- .../kscience/visionforge/solid/Solids.kt | 3 +- .../visionforge/solid/three/ThreeCanvas.kt | 13 ++- .../visionforge/solid/three/ThreePlugin.kt | 26 ++--- 32 files changed, 136 insertions(+), 396 deletions(-) delete mode 100644 ui/material/build.gradle.kts delete mode 100644 ui/material/src/main/kotlin/hep/dataforge/vision/material/icons.kt delete mode 100644 ui/material/src/main/kotlin/hep/dataforge/vision/material/misc.kt delete mode 100644 ui/material/src/main/kotlin/hep/dataforge/vision/material/objectTree.kt delete mode 100644 ui/material/src/main/kotlin/materialui/components/slider/builder.kt delete mode 100644 ui/material/src/main/kotlin/materialui/components/slider/enums.kt delete mode 100644 ui/material/src/main/kotlin/materialui/components/slider/slider.kt diff --git a/demo/gdml/build.gradle.kts b/demo/gdml/build.gradle.kts index cf9d8eb3..31232cc8 100644 --- a/demo/gdml/build.gradle.kts +++ b/demo/gdml/build.gradle.kts @@ -20,7 +20,6 @@ kotlin { useCommonJs() browser { commonWebpackConfig { - sourceMaps = false cssSupport.enabled = false } } diff --git a/demo/jupyter-playground/src/main/kotlin/hep/dataforge/playground/VisionForgePlayGroundForJupyter.kt b/demo/jupyter-playground/src/main/kotlin/hep/dataforge/playground/VisionForgePlayGroundForJupyter.kt index e1e2a97f..88e4effb 100644 --- a/demo/jupyter-playground/src/main/kotlin/hep/dataforge/playground/VisionForgePlayGroundForJupyter.kt +++ b/demo/jupyter-playground/src/main/kotlin/hep/dataforge/playground/VisionForgePlayGroundForJupyter.kt @@ -18,7 +18,7 @@ import space.kscience.visionforge.html.HtmlVisionFragment import space.kscience.visionforge.html.Page import space.kscience.visionforge.html.embedVisionFragment import space.kscience.visionforge.plotly.PlotlyPlugin -import space.kscience.visionforge.plotly.toVision +import space.kscience.visionforge.plotly.asVision import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.visionManager @@ -80,7 +80,7 @@ internal class VisionForgePlayGroundForJupyter : JupyterIntegration() { render { plot -> val fragment = HtmlVisionFragment { - vision(plot.toVision()) + vision(plot.asVision()) } HTML(produceHtmlVisionString(fragment)) diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 43f13028..c485a1e3 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -3,11 +3,13 @@ package ru.mipt.npm.muon.monitor import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z +import space.kscience.visionforge.VisionManager import space.kscience.visionforge.removeAll +import space.kscience.visionforge.root import space.kscience.visionforge.solid.* import kotlin.math.PI -class Model { +class Model(val manager: VisionManager) { private val map = HashMap() private val events = HashSet() @@ -34,6 +36,7 @@ class Model { var tracks: SolidGroup val root: SolidGroup = SolidGroup().apply { + root(this@Model.manager) rotationX = PI / 2 group("bottom") { Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach { @@ -78,7 +81,5 @@ class Model { } } - companion object { - fun buildGeometry() = Model().root - } + fun encodeToString(): String = manager.encodeToString(this.root) } \ No newline at end of file diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt index 45a34e06..2ae42bbb 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt @@ -7,13 +7,16 @@ import kotlinx.browser.document import react.child import react.dom.render import space.kscience.dataforge.context.Global +import space.kscience.dataforge.context.fetch import space.kscience.visionforge.Application +import space.kscience.visionforge.VisionManager import space.kscience.visionforge.bootstrap.useBootstrap import space.kscience.visionforge.startApplication private class MMDemoApp : Application { - private val model = Model() + private val visionManager = Global.fetch(VisionManager) + private val model = Model(visionManager) private val connection = HttpClient { install(JsonFeature) { diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt index d22a762d..29abd7ca 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt @@ -52,7 +52,7 @@ fun Application.module(context: Context = Global) { } get("/geometry") { call.respondText( - solidManager.visionManager.encodeToString(Model.buildGeometry()), + Model(solidManager.visionManager).encodeToString(), contentType = ContentType.Application.Json, status = HttpStatusCode.OK ) diff --git a/demo/sat-demo/build.gradle.kts b/demo/sat-demo/build.gradle.kts index c2d4c852..267e4a82 100644 --- a/demo/sat-demo/build.gradle.kts +++ b/demo/sat-demo/build.gradle.kts @@ -15,8 +15,9 @@ group = "ru.mipt.npm" dependencies{ implementation(project(":visionforge-threejs:visionforge-threejs-server")) + implementation("ch.qos.logback:logback-classic:1.2.3") } application { - mainClass.set("ru.mipt.npm.gradle.sat.SatServerKt") + mainClass.set("ru.mipt.npm.sat.SatServerKt") } diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index 18c56704..f084127d 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -1,6 +1,9 @@ package space.kscience.visionforge.solid.demo -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.names.toName @@ -73,7 +76,7 @@ fun VisionLayout.showcase() { //override color for this cube color(1530) - GlobalScope.launch(Dispatchers.Main) { + launch(Dispatchers.Main) { while (isActive) { delay(500) visible = !(visible ?: false) @@ -82,7 +85,7 @@ fun VisionLayout.showcase() { } } - GlobalScope.launch(Dispatchers.Main) { + launch(Dispatchers.Main) { val random = Random(111) while (isActive) { delay(1000) diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt index b26e30aa..18e23381 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt @@ -19,6 +19,7 @@ import space.kscience.visionforge.VisionLayout import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.three.ThreeCanvas import space.kscience.visionforge.solid.three.ThreePlugin +import space.kscience.visionforge.solid.three.configure class ThreeDemoGrid(element: Element) : VisionLayout { private lateinit var navigationElement: HTMLElement @@ -70,7 +71,7 @@ class ThreeDemoGrid(element: Element) : VisionLayout { } } val element = document.getElementById("output-$name") ?: error("Element not found") - three.createCanvas(element, canvasOptions) + three.getOrCreateCanvas(element).also { it.configure(canvasOptions) } }.render(vision) } } diff --git a/ui/material/build.gradle.kts b/ui/material/build.gradle.kts deleted file mode 100644 index b68d5df2..00000000 --- a/ui/material/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("ru.mipt.npm.gradle.js") -} - -val dataforgeVersion: String by rootProject.extra - -dependencies{ - api(project(":ui:react")) - - api("subroh0508.net.kotlinmaterialui:core:0.4.5") - api("subroh0508.net.kotlinmaterialui:lab:0.4.5") - api(npm("@material-ui/core","4.9.14")) - api(npm("@material-ui/lab","4.0.0-alpha.51")) - //api(npm("@material-ui/icons","4.9.1")) -} \ No newline at end of file diff --git a/ui/material/src/main/kotlin/hep/dataforge/vision/material/icons.kt b/ui/material/src/main/kotlin/hep/dataforge/vision/material/icons.kt deleted file mode 100644 index 1ad3f280..00000000 --- a/ui/material/src/main/kotlin/hep/dataforge/vision/material/icons.kt +++ /dev/null @@ -1,11 +0,0 @@ -package space.kscience.visionforge.material - -//@JsModule("@material-ui/icons/ExpandMore") -//external class ExpandMoreIcon : Component{ -// override fun render(): dynamic -//} -// -//@JsModule("@material-ui/icons/ChevronRight") -//external class ChevronRightIcon : Component{ -// override fun render(): dynamic -//} \ No newline at end of file diff --git a/ui/material/src/main/kotlin/hep/dataforge/vision/material/misc.kt b/ui/material/src/main/kotlin/hep/dataforge/vision/material/misc.kt deleted file mode 100644 index a093c043..00000000 --- a/ui/material/src/main/kotlin/hep/dataforge/vision/material/misc.kt +++ /dev/null @@ -1,97 +0,0 @@ -package space.kscience.visionforge.material - -import space.kscience.visionforge.react.component -import space.kscience.visionforge.react.state -import kotlinx.html.DIV -import materialui.components.card.card -import materialui.components.cardcontent.cardContent -import materialui.components.cardheader.cardHeader -import materialui.components.container.container -import materialui.components.container.enums.ContainerMaxWidth -import materialui.components.expansionpanel.expansionPanel -import materialui.components.expansionpaneldetails.expansionPanelDetails -import materialui.components.expansionpanelsummary.expansionPanelSummary -import materialui.components.grid.GridElementBuilder -import materialui.components.grid.enums.GridDirection -import materialui.components.grid.enums.GridStyle -import materialui.components.grid.grid -import materialui.components.paper.paper -import materialui.components.typography.typographyH5 -import react.RBuilder -import react.RProps -import react.child -import react.dom.RDOMBuilder - - -fun accordionComponent(elements: List.() -> Unit>>) = - component { - val expandedIndex: Int? by state { null } - - container { - attrs { - maxWidth = ContainerMaxWidth.`false` - } - elements.forEachIndexed { index, (header, body) -> - expansionPanel { - attrs { - expanded = index == expandedIndex - } - expansionPanelSummary { - typographyH5 { - +header - } - } - expansionPanelDetails { - this.body() - } - } - } - } - } - -typealias RAccordionBuilder = MutableList.() -> Unit>> - -fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder
.() -> Unit) { - add(title to builder) -} - -fun RBuilder.accordion(builder: RAccordionBuilder.() -> Unit) { - val list: List.() -> Unit>> = - ArrayList.() -> Unit>>().apply(builder) - child(accordionComponent(list)) -} - -fun RBuilder.materialCard(title: String, block: RBuilder.() -> Unit) { - card { - cardHeader { - attrs { - this.title = typographyH5 { - +title - } - } - } - cardContent { - paper { - this.block() - } - } - } -} - -fun RBuilder.column(vararg classMap: Pair, block: GridElementBuilder
.() -> Unit) = - grid(*classMap) { - attrs { - container = true - direction = GridDirection.column - } - block() - } - -fun RBuilder.row(vararg classMap: Pair, block: GridElementBuilder
.() -> Unit) = - grid(*classMap) { - attrs { - container = true - direction = GridDirection.row - } - block() - } diff --git a/ui/material/src/main/kotlin/hep/dataforge/vision/material/objectTree.kt b/ui/material/src/main/kotlin/hep/dataforge/vision/material/objectTree.kt deleted file mode 100644 index d90d2904..00000000 --- a/ui/material/src/main/kotlin/hep/dataforge/vision/material/objectTree.kt +++ /dev/null @@ -1,94 +0,0 @@ -package space.kscience.visionforge.material - -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.plus -import space.kscience.dataforge.names.toName -import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionGroup -import space.kscience.visionforge.isEmpty -import space.kscience.visionforge.react.component -import space.kscience.visionforge.react.state -import kotlinx.html.UL -import materialui.lab.components.treeItem.treeItem -import materialui.lab.components.treeView.SingleSelectTreeViewElementBuilder -import materialui.lab.components.treeView.treeView -import react.FunctionalComponent -import react.RBuilder -import react.RProps -import react.child -import react.dom.span - -interface ObjectTreeProps : RProps { - var name: Name - var selected: Name? - var obj: Vision - var clickCallback: (Name?) -> Unit -} - -private fun RBuilder.treeBranch(name: Name, obj: Vision): Unit { - treeItem { - val token = name.last()?.toString() ?: "World" - attrs { - nodeId = name.toString() - label { - span { - +token - } - } - } - - if (obj is VisionGroup) { - obj.children.entries - .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children - .sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true } - .forEach { (childToken, child) -> - treeBranch(name + childToken, child) - } - } - } -} - -val ObjectTree: FunctionalComponent = component { props -> - var selected: String? by state { props.selected.toString() } - treeView { - this as SingleSelectTreeViewElementBuilder
    - attrs { - this.selected = selected - this.onNodeSelect{ _, selectedItem -> - selected = selectedItem - val itemName = selected?.toName() - props.clickCallback(itemName) - Unit - } - defaultCollapseIcon { - span{ - +"-" - } - //child(ExpandMoreIcon::class) {} - }//{} - defaultExpandIcon { - span{ - +"+" - } - //child(ChevronRightIcon::class) {} - }//{} - } - treeBranch(props.name, props.obj) - } -} - -fun RBuilder.objectTree( - vision: Vision, - selected: Name? = null, - clickCallback: (Name?) -> Unit = {} -) { - child(ObjectTree) { - attrs { - this.name = Name.EMPTY - this.obj = vision - this.selected = selected - this.clickCallback = clickCallback - } - } -} - diff --git a/ui/material/src/main/kotlin/materialui/components/slider/builder.kt b/ui/material/src/main/kotlin/materialui/components/slider/builder.kt deleted file mode 100644 index 3e764c15..00000000 --- a/ui/material/src/main/kotlin/materialui/components/slider/builder.kt +++ /dev/null @@ -1,37 +0,0 @@ -package materialui.components.slider - -import kotlinx.html.DIV -import kotlinx.html.Tag -import materialui.components.MaterialElementBuilder -import materialui.components.getValue -import materialui.components.inputbase.enums.InputBaseStyle -import materialui.components.setValue -import react.RClass -import react.ReactElement - -class SliderElementBuilder internal constructor( - type: RClass, - classMap: List, String>> -) : MaterialElementBuilder(type, classMap, { DIV(mapOf(), it) }) { - fun Tag.classes(vararg classMap: Pair) { - classes(classMap.toList()) - } - var Tag.defaultValue: Number? by materialProps - var Tag.disabled: Boolean? by materialProps// = false - var Tag.getAriaLabel: String? by materialProps - var Tag.getAriaValueText: String? by materialProps - var Tag.marks: Array? by materialProps - var Tag.max: Number? by materialProps// = 100 - var Tag.min: Number? by materialProps// = 0, - var Tag.name: String? by materialProps - var Tag.onChange: ((dynamic, Number) -> Unit)? by materialProps - var Tag.onChangeCommitted: ((dynamic, Number) -> Unit)? by materialProps - var Tag.orientation: SliderOrientation? by materialProps - var Tag.scale: ((Number) -> Number)? by materialProps// {it} - var Tag.step: Number? by materialProps// = 1, - //ThumbComponent = 'span', - var Tag.track: SliderTrack by materialProps - var Tag.value: Number? by materialProps - var Tag.ValueLabelComponent: ReactElement? by materialProps - var Tag.valueLabelDisplay: SliderValueLabelDisplay by materialProps -} \ No newline at end of file diff --git a/ui/material/src/main/kotlin/materialui/components/slider/enums.kt b/ui/material/src/main/kotlin/materialui/components/slider/enums.kt deleted file mode 100644 index 8a8f9148..00000000 --- a/ui/material/src/main/kotlin/materialui/components/slider/enums.kt +++ /dev/null @@ -1,18 +0,0 @@ -package materialui.components.slider - -enum class SliderOrientation { - horizontal, - vertical -} - -enum class SliderTrack{ - normal, - `false`, - inverted, -} - -enum class SliderValueLabelDisplay{ - on, - auto, - off -} \ No newline at end of file diff --git a/ui/material/src/main/kotlin/materialui/components/slider/slider.kt b/ui/material/src/main/kotlin/materialui/components/slider/slider.kt deleted file mode 100644 index 12a8f8e0..00000000 --- a/ui/material/src/main/kotlin/materialui/components/slider/slider.kt +++ /dev/null @@ -1,38 +0,0 @@ -package materialui.components.slider - -import materialui.components.StandardProps -import materialui.components.input.enums.InputStyle -import react.RBuilder -import react.RClass -import react.ReactElement - -@JsModule("@material-ui/core/Slider") -private external val sliderModule: dynamic - -external interface SliderProps : StandardProps { - var defaultValue: Number? - var disabled: Boolean?// = false - var getAriaLabel: String? - var getAriaValueText: String? - var marks: Array? - var max: Number?// = 100 - var min: Number?// = 0, - var name: String? - var onChange: ((dynamic, Number) -> Unit)? - var onChangeCommitted: ((dynamic, Number) -> Unit)? - var orientation: SliderOrientation? - var scale: ((Number) -> Number)?// {it} - var step: Number? // = 1, - //ThumbComponent = 'span', - var track: SliderTrack - var value: Number? - var ValueLabelComponent: ReactElement? - var valueLabelDisplay: SliderValueLabelDisplay - //valueLabelFormat = Identity, -} - -@Suppress("UnsafeCastFromDynamic") -private val sliderComponent: RClass = sliderModule.default - -fun RBuilder.slider(vararg classMap: Pair, block: SliderElementBuilder.() -> Unit) = - child(SliderElementBuilder(sliderComponent, classMap.toList()).apply(block).create()) diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt index 4f645bf6..f18854a6 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt @@ -14,6 +14,7 @@ import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.three.ThreeCanvas import space.kscience.visionforge.solid.three.ThreePlugin +import space.kscience.visionforge.solid.three.configure import styled.css import styled.styledDiv @@ -36,12 +37,15 @@ public val ThreeCanvasComponent: FunctionalComponent = functio val elementRef = useRef(null) var canvas by useState(null) - val three: ThreePlugin = useMemo({props.context.fetch(ThreePlugin)}, arrayOf(props.context)) + val three: ThreePlugin = useMemo({ props.context.fetch(ThreePlugin) }, arrayOf(props.context)) useEffect(listOf(props.obj, props.options, elementRef)) { if (canvas == null) { val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") - val newCanvas: ThreeCanvas = three.createCanvas(element, props.options ?: Canvas3DOptions.empty()) + val newCanvas: ThreeCanvas = three.getOrCreateCanvas(element) + props.options?.let { + newCanvas.configure(it) + } props.canvasCallback?.invoke(newCanvas) canvas = newCanvas } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index 9ca20f6b..23779bd1 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -13,18 +13,25 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.toName import space.kscience.visionforge.Vision.Companion.TYPE +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * A root type for display hierarchy */ @Type(TYPE) -public interface Vision : Described { +public interface Vision : Described, CoroutineScope { /** * The parent object of this one. If null, this one is a root. */ public var parent: VisionGroup? + public val manager: VisionManager? get() = parent?.manager + + override val coroutineContext: CoroutineContext + get() = manager?.context?.coroutineContext ?: EmptyCoroutineContext + /** * Get property. * @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false. diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 26ec2523..47ed889e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -1,7 +1,5 @@ package space.kscience.visionforge -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch @@ -30,14 +28,14 @@ internal data class PropertyListener( */ @Serializable @SerialName("vision") -public open class VisionBase( - internal var properties: Config? = null, - @Transient override var parent: VisionGroup? = null, - @Transient public val coroutineScope: CoroutineScope = GlobalScope, -) : Vision { +public open class VisionBase : Vision { + protected var properties: Config? = null + + @Transient + override var parent: VisionGroup? = null @Synchronized - protected fun getOrCreateConfig(): Config { + protected fun getOrCreateProperties(): Config { if (properties == null) { val newProperties = Config() properties = newProperties @@ -77,7 +75,7 @@ public open class VisionBase( } override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - getOrCreateConfig().setItem(name, item) + getOrCreateProperties().setItem(name, item) if (notify) { invalidateProperty(name) } @@ -104,7 +102,7 @@ public open class VisionBase( get() = propertyInvalidationFlow override fun invalidateProperty(propertyName: Name) { - coroutineScope.launch { + launch { if (propertyName == STYLE_KEY) { updateStyles(styles) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index fa22d072..4f16982b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -79,6 +79,7 @@ public operator fun VisionContainer.get(str: String): V? = get(s public operator fun VisionContainerBuilder.set(token: NameToken, child: V?): Unit = set(token.asName(), child) -public operator fun VisionContainerBuilder.set(key: String?, child: V?): Unit = set(key?.toName(), child) +public operator fun VisionContainerBuilder.set(key: String?, child: V?): Unit = + set(key?.toName(), child) public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index f7f703ab..3e43c81d 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -48,7 +48,7 @@ public open class VisionGroupBase( * Propagate children change event upwards */ private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - coroutineScope.launch { + launch { _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) } } @@ -141,4 +141,16 @@ public open class VisionGroupBase( } super.update(change) } +} + +/** + * Non-serializable root group used to propagate manager to its children + */ +internal class RootVisionGroup(override val manager: VisionManager) : VisionGroupBase() + +/** + * Designate this [VisionGroup] as a root group and assign a [VisionManager] as its parent + */ +public fun Vision.root(manager: VisionManager){ + parent = RootVisionGroup(manager) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt index 2e902d4d..e45e4f08 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt @@ -14,7 +14,7 @@ public fun TagConsumer<*>.embedVisionFragment( fragment: HtmlVisionFragment, ): Map { val visionMap = HashMap() - val consumer = object : VisionTagConsumer(this@embedVisionFragment, idPrefix) { + val consumer = object : VisionTagConsumer(this@embedVisionFragment, manager, idPrefix) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { visionMap[name] = vision script { @@ -34,13 +34,13 @@ public fun FlowContent.embedVisionFragment( manager: VisionManager, idPrefix: String? = null, fragment: HtmlVisionFragment, -): Map = consumer.embedVisionFragment(manager, idPrefix, fragment) +): Map = consumer.embedVisionFragment(manager, idPrefix, fragment) internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById" @DFExperimental -public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment){ +public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment) { div { div { this.id = id.toString() diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index 967e1851..18c53e4f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -17,7 +17,7 @@ import kotlin.collections.set * A placeholder object to attach inline vision builders. */ @DFExperimental -public class VisionOutput @PublishedApi internal constructor() { +public class VisionOutput @PublishedApi internal constructor(public val manager: VisionManager) { public var meta: Meta = Meta.EMPTY //TODO expose a way to define required plugins. @@ -32,6 +32,7 @@ public class VisionOutput @PublishedApi internal constructor() { */ public abstract class VisionTagConsumer( private val root: TagConsumer, + public val manager:VisionManager, private val idPrefix: String? = null, ) : TagConsumer by root { @@ -62,7 +63,7 @@ public abstract class VisionTagConsumer( script { attributes["class"] = OUTPUT_META_CLASS unsafe { - +VisionManager.defaultJson.encodeToString(MetaSerializer, outputMeta) + +manager.jsonFormat.encodeToString(MetaSerializer, outputMeta) } } } @@ -76,7 +77,7 @@ public abstract class VisionTagConsumer( name: Name, visionProvider: VisionOutput.() -> Vision, ): T { - val output = VisionOutput() + val output = VisionOutput(manager) val vision = output.visionProvider() return vision(name, vision, output.meta) } diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index e50c13b3..1199b89b 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -2,14 +2,13 @@ package space.kscience.visionforge.html import kotlinx.html.* import kotlinx.html.stream.createHTML +import space.kscience.dataforge.context.Global +import space.kscience.dataforge.context.fetch import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.set import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionBase -import space.kscience.visionforge.configure -import space.kscience.visionforge.meta +import space.kscience.visionforge.* import kotlin.test.Test typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit @@ -20,7 +19,7 @@ fun FlowContent.renderVisionFragment( fragment: HtmlVisionFragment, ): Map { val visionMap = HashMap() - val consumer = object : VisionTagConsumer(consumer, idPrefix) { + val consumer = object : VisionTagConsumer(consumer, Global.fetch(VisionManager), idPrefix) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { visionMap[name] = vision renderer(name, vision, outputMeta) @@ -37,7 +36,7 @@ class HtmlTagTest { fun VisionOutput.base(block: VisionBase.() -> Unit) = VisionBase().apply(block) - val fragment: HtmlVisionFragment = { + val fragment: HtmlVisionFragment = { div { h1 { +"Head" } vision("ddd") { @@ -72,7 +71,7 @@ class HtmlTagTest { @Test fun testStringRender() { println( - createHTML().div{ + createHTML().div { renderVisionFragment(simpleVisionRenderer, fragment = fragment) } ) diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index 22d670dd..98803115 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -128,7 +128,7 @@ public class VisionClient : AbstractPlugin() { logger.info { "Updating vision data from $wsUrl" } - val ws = WebSocket(wsUrl.toString()).apply { + WebSocket(wsUrl.toString()).apply { onmessage = { messageEvent -> val stringData: String? = messageEvent.data as? String if (stringData != null) { diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/elementOutput.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/elementOutput.kt index 555e4627..f85fdabb 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/elementOutput.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/elementOutput.kt @@ -17,7 +17,7 @@ public interface ElementVisionRenderer { /** * Display the [vision] inside a given [element] replacing its current content */ - public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY): Unit + public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY) public companion object { public const val TYPE: String = "elementVisionRenderer" diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts index ea0aa763..b62876ab 100644 --- a/visionforge-plotly/build.gradle.kts +++ b/visionforge-plotly/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.mpp") } -val plotlyVersion = "0.4.2" +val plotlyVersion = "0.4.3" kscience { useSerialization() diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index c9d6db78..773ff4bf 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge.plotly +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.Config import space.kscience.dataforge.misc.DFExperimental @@ -7,13 +8,23 @@ import space.kscience.plotly.Plot import space.kscience.plotly.Plotly import space.kscience.visionforge.VisionBase import space.kscience.visionforge.html.VisionOutput +import space.kscience.visionforge.root @Serializable -public class VisionOfPlotly(private val plotConfig: Config) : VisionBase(plotConfig){ - public val plot: Plot get() = Plot(plotConfig) +@SerialName("vision.plotly") +public class VisionOfPlotly private constructor() : VisionBase() { + public constructor(plot: Plot) : this() { + properties = plot.config + } + + public val plot: Plot get() = Plot(properties ?: Config()) } -public fun Plot.toVision(): VisionOfPlotly = VisionOfPlotly(config) +public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) @DFExperimental -public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block).config) \ No newline at end of file +public inline fun VisionOutput.plotly( + block: Plot.() -> Unit, +): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)).apply { + root(this@plotly.manager) +} \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt index 317a6930..be9e1e08 100644 --- a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt +++ b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt @@ -31,6 +31,8 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val config = PlotlyConfig.read(meta) + println(plot.config) + println(plot.data[0].toMeta()) element.plot(plot, config) } diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index 1c398e74..6f0a3959 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -43,12 +43,6 @@ import java.net.URI import kotlin.collections.set import kotlin.time.Duration -public enum class VisionServerDataMode { - EMBED, - FETCH, - CONNECT -} - /** * A ktor plugin container with given [routing] @@ -61,7 +55,9 @@ public class VisionServer internal constructor( 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) - public var dataMode: VisionServerDataMode = VisionServerDataMode.CONNECT + public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName()) + public var dataFetch: Boolean by config.boolean(false, "data.fetch".toName()) + public var dataConnect: Boolean by config.boolean(true, "data.connect".toName()) /** * a list of headers that should be applied to all pages @@ -79,25 +75,26 @@ public class VisionServer internal constructor( ): Map { val visionMap = HashMap() - val consumer = object : VisionTagConsumer(consumer) { + val consumer = object : VisionTagConsumer(consumer, visionManager) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { visionMap[name] = vision // Toggle update mode - when (dataMode) { - VisionServerDataMode.EMBED -> { - script { - attributes["class"] = OUTPUT_DATA_CLASS - unsafe { - +"\n${visionManager.encodeToString(vision)}\n" - } + if (dataConnect) { + attributes[OUTPUT_CONNECT_ATTRIBUTE] = "auto" + } + + if (dataFetch) { + attributes[OUTPUT_FETCH_ATTRIBUTE] = "auto" + } + + if (dataEmbed) { + script { + type = "text/json" + attributes["class"] = OUTPUT_DATA_CLASS + unsafe { + +"\n${visionManager.encodeToString(vision)}\n" } } - VisionServerDataMode.FETCH -> { - attributes[OUTPUT_FETCH_ATTRIBUTE] = "auto" - } - VisionServerDataMode.CONNECT -> { - attributes[OUTPUT_CONNECT_ATTRIBUTE] = "auto" - } } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt index 1a83d2cb..67224699 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt @@ -68,4 +68,5 @@ public class Solids(meta: Meta) : VisionPlugin(meta) { @VisionBuilder @DFExperimental -public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) +public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = + SolidGroup().apply(block).apply { root(this@solid.manager) } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt index f5629cbd..e62b4cb2 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt @@ -24,9 +24,7 @@ import org.w3c.dom.Node import org.w3c.dom.events.MouseEvent import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.meta.useProperty +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.* import space.kscience.visionforge.Colors import space.kscience.visionforge.solid.Solid @@ -41,8 +39,8 @@ import kotlin.math.sin */ public class ThreeCanvas( public val three: ThreePlugin, - public val options: Canvas3DOptions, ) { + public val options = Canvas3DOptions() private var boundingBox: Box3? = null private var root: Object3D? = null @@ -315,4 +313,11 @@ public class ThreeCanvas( private const val AXES_NAME = "@axes" private const val CLIP_HELPER_NAME = "@clipping" } +} + +public fun ThreeCanvas.configure(options: Canvas3DOptions) { + this.options.update(options.toMeta()) + options.onChange(this) { name, _, newItem -> + options[name] = newItem + } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index da3a94b6..3c4e8d7a 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -8,6 +8,7 @@ import org.w3c.dom.Element import org.w3c.dom.HTMLElement import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.update import space.kscience.dataforge.names.* import space.kscience.visionforge.ElementVisionRenderer import space.kscience.visionforge.Vision @@ -111,12 +112,13 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } } - public fun createCanvas( + private val canvasCache = HashMap() + + public fun getOrCreateCanvas( element: Element, - options: Canvas3DOptions = Canvas3DOptions.empty(), - ): ThreeCanvas = ThreeCanvas(this, options).apply { + ): ThreeCanvas = canvasCache.getOrPut(element){ThreeCanvas(this).apply { attach(element) - } + }} override fun content(target: String): Map { return when (target) { @@ -128,11 +130,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { override fun rateVision(vision: Vision): Int = if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING - public fun renderSolid( + fun renderSolid( element: Element, vision: Solid, - options: Canvas3DOptions, - ): ThreeCanvas = createCanvas(element, options).apply { + ): ThreeCanvas = getOrCreateCanvas(element).apply { render(vision) } @@ -140,8 +141,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { renderSolid( element, vision as? Solid ?: error("Solid expected but ${vision::class} found"), - Canvas3DOptions.read(meta) - ) + ).apply { + options.update(meta) + } } public companion object : PluginFactory { @@ -154,8 +156,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { public fun ThreePlugin.render( element: HTMLElement, obj: Solid, - options: Canvas3DOptions.() -> Unit = {}, -): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(options)) + optionsBuilder: Canvas3DOptions.() -> Unit = {}, +): ThreeCanvas = renderSolid(element, obj).apply { + options.apply(optionsBuilder) +} internal operator fun Object3D.set(token: NameToken, object3D: Object3D) { object3D.name = token.toString()