From dfc0ffda38c4f8a750bf221549a11d79ea4b7d31 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 8 Jun 2021 18:45:03 +0300 Subject: [PATCH] Fix crash with single style properties --- .../visionforge/gdml/demo/GDMLAppComponent.kt | 91 ++++++++++--------- .../visionforge/gdml/demo/fileDrop.kt | 1 + demo/js-playground/build.gradle.kts | 27 ++++++ .../src/main/kotlin/JsPlaygroundApp.kt | 51 +++++++++++ .../src/main/resources/index.html | 11 +++ .../js-playground/webpack.config.d/01.ring.js | 3 + .../mipt/npm/muon/monitor/MMAppComponent.kt | 27 +++--- .../ru/mipt/npm/muon/monitor/MMDemoApp.kt | 3 +- .../playground/src/jvmMain/kotlin/gdmlIaxo.kt | 3 +- settings.gradle.kts | 3 +- .../kscience/visionforge/react/MetaViewer.kt | 7 +- .../visionforge/react/PropertyEditor.kt | 1 + .../visionforge/react/RangeValueChooser.kt | 58 +++++++++--- .../visionforge/react/ThreeCanvasComponent.kt | 23 +++-- .../visionforge/react/valueChooser.kt | 1 + .../src/main/kotlin/ringui/tabs/SmartTabs.kt | 8 +- ui/ring/src/main/kotlin/ringui/tabs/Tabs.kt | 3 +- .../ThreeViewWithControls.kt | 11 +-- .../ThreeWithControls.kt | 4 +- .../ringPropertyEditor.kt | 69 +++++++++----- .../ringThreeControls.kt | 29 +++--- .../visionforge/solid/SolidReference.kt | 24 +++-- .../visionforge/solid/three/ThreeCanvas.kt | 32 ++++--- 23 files changed, 331 insertions(+), 159 deletions(-) create mode 100644 demo/js-playground/build.gradle.kts create mode 100644 demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt create mode 100644 demo/js-playground/src/main/resources/index.html create mode 100644 demo/js-playground/webpack.config.d/01.ring.js diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt index 0a0e83b9..cf115e5e 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt @@ -1,17 +1,20 @@ package space.kscience.visionforge.gdml.demo import kotlinx.browser.window -import kotlinx.css.* +import kotlinx.css.height +import kotlinx.css.vh import org.w3c.files.FileReader import org.w3c.files.get import react.* import react.dom.h1 +import ringui.grid.ringCol +import ringui.grid.ringGrid +import ringui.grid.ringRow import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import space.kscience.dataforge.names.Name import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromString -import space.kscience.visionforge.bootstrap.gridRow import space.kscience.visionforge.bootstrap.nameCrumbs import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.react.ThreeCanvasComponent @@ -21,7 +24,6 @@ import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.specifications.Canvas3DOptions import styled.css -import styled.styledDiv external interface GDMLAppProps : RProps { var context: Context @@ -62,54 +64,57 @@ val GDMLApp = functionalComponent("GDMLApp") { props -> vision = parsedVision as? Solid ?: error("Parsed vision is not a solid") } - gridRow { - flexColumn { - css { - +"col-lg-9" - height = 100.vh - } - styledDiv { - css { - +"mx-auto" - +"page-header" - } - h1 { +"GDML/JSON loader demo" } - } - nameCrumbs(selected, "World", onSelect) - //canvas - child(ThreeCanvasComponent) { + ringGrid { + ringRow { + ringCol { attrs { - this.context = props.context - this.obj = vision - this.selected = selected - this.options = options + lg = 9 } - } + flexColumn { + css { + height = 100.vh + } + h1 { +"GDML/JSON loader demo" } + //canvas - } - flexColumn { - css { - +"col-lg-3" - padding(top = 4.px) - //border(1.px, BorderStyle.solid, Color.lightGray) - height = 100.vh - overflowY = Overflow.auto - } - fileDrop("(drag file here)") { files -> - val file = files?.get(0) - if (file != null) { - - FileReader().apply { - onload = { - val string = result as String - loadData(file.name, string) + child(ThreeCanvasComponent) { + attrs { + this.context = props.context + this.solid = vision + this.selected = selected + this.options = options } - readAsText(file) } } + + } + ringCol { + attrs { + lg = 3 + } + flexColumn { + css { + height = 100.vh + } + fileDrop("(drag file here)") { files -> + val file = files?.get(0) + if (file != null) { + + FileReader().apply { + onload = { + val string = result as String + loadData(file.name, string) + } + readAsText(file) + } + } + } + nameCrumbs(selected, "World", onSelect) + ringThreeControls(options, vision, selected, onSelect) + } } - ringThreeControls(options, vision, selected, onSelect) } } } + diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/fileDrop.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/fileDrop.kt index dcd7b4eb..e91fedee 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/fileDrop.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/fileDrop.kt @@ -14,6 +14,7 @@ fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) { styledDiv { css { border(style = BorderStyle.dashed, width = 1.px, color = Color.orange) + flexGrow = 0.0 alignContent = Align.center } diff --git a/demo/js-playground/build.gradle.kts b/demo/js-playground/build.gradle.kts new file mode 100644 index 00000000..4a908719 --- /dev/null +++ b/demo/js-playground/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("ru.mipt.npm.gradle.js") +} + +kscience{ + useCoroutines() + application() +} + +kotlin{ + js(IR){ + useCommonJs() + browser { + commonWebpackConfig { + cssSupport.enabled = false + } + } + } +} + + +dependencies{ + implementation(project(":visionforge-gdml")) + implementation(project(":visionforge-plotly")) + implementation(project(":visionforge-threejs")) + implementation(project(":ui:ring")) +} \ No newline at end of file diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt new file mode 100644 index 00000000..8fa66e74 --- /dev/null +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -0,0 +1,51 @@ +import kotlinx.browser.document +import kotlinx.css.height +import kotlinx.css.vh +import kotlinx.css.vw +import kotlinx.css.width +import react.child +import react.dom.render +import space.kscience.dataforge.context.Context +import space.kscience.gdml.GdmlShowCase +import space.kscience.visionforge.Application +import space.kscience.visionforge.VisionClient +import space.kscience.visionforge.gdml.toVision +import space.kscience.visionforge.ring.ThreeCanvasWithControls +import space.kscience.visionforge.ring.ThreeWithControls +import space.kscience.visionforge.startApplication +import styled.css +import styled.styledDiv + +private class JsPlaygroundApp : Application { + + override fun start(state: Map) { + + val playgroundContext = Context { + plugin(ThreeWithControls) + plugin(VisionClient) + } + + val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page") + + val visionOfD0 = GdmlShowCase.babyIaxo().toVision() + + render(element) { + styledDiv { + css{ + height = 100.vh + width = 100.vw + } + child(ThreeCanvasWithControls) { + attrs { + context = playgroundContext + solid = visionOfD0 + } + } + } + } + } +} + +public fun main() { + startApplication(::JsPlaygroundApp) +} \ No newline at end of file diff --git a/demo/js-playground/src/main/resources/index.html b/demo/js-playground/src/main/resources/index.html new file mode 100644 index 00000000..7a777ef1 --- /dev/null +++ b/demo/js-playground/src/main/resources/index.html @@ -0,0 +1,11 @@ + + + + + js-playground + + + +
+ + \ No newline at end of file diff --git a/demo/js-playground/webpack.config.d/01.ring.js b/demo/js-playground/webpack.config.d/01.ring.js new file mode 100644 index 00000000..41da041c --- /dev/null +++ b/demo/js-playground/webpack.config.d/01.ring.js @@ -0,0 +1,3 @@ +const ringConfig = require('@jetbrains/ring-ui/webpack.config').config; + +config.module.rules.push(...ringConfig.module.rules) \ No newline at end of file diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index 368bef5f..f8d2030c 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -34,14 +34,6 @@ external interface MMAppProps : RProps { var selected: Name? } -private val canvasConfig = Canvas3DOptions { - camera = Camera { - distance = 2100.0 - latitude = PI / 6 - azimuth = PI + PI / 6 - } -} - @JsExport val MMApp = functionalComponent("Muon monitor") { props -> var selected by useState { props.selected } @@ -50,8 +42,13 @@ val MMApp = functionalComponent("Muon monitor") { props -> selected = it } - val options = useMemo { - Canvas3DOptions.invoke { + val mmOptions = useMemo { + Canvas3DOptions { + camera = Camera { + distance = 2100.0 + latitude = PI / 6 + azimuth = PI + PI / 6 + } this.onSelect = onSelect } } @@ -91,11 +88,9 @@ val MMApp = functionalComponent("Muon monitor") { props -> child(ThreeCanvasComponent) { attrs { this.context = props.context - this.obj = root + this.solid = root this.selected = selected - this.options = canvasConfig.apply { - this.onSelect = onSelect - } + this.options = mmOptions } } } @@ -112,7 +107,7 @@ val MMApp = functionalComponent("Muon monitor") { props -> } //settings card("Canvas configuration") { - canvasControls(options, root) + canvasControls(mmOptions, root) } card("Events") { @@ -151,7 +146,7 @@ val MMApp = functionalComponent("Muon monitor") { props -> +"World" attrs { onClickFunction = { - selected = space.kscience.dataforge.names.Name.EMPTY + selected = Name.EMPTY } } } 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 2ae42bbb..5c4a589a 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 @@ -6,6 +6,7 @@ import io.ktor.client.features.json.serializer.KotlinxSerializer import kotlinx.browser.document import react.child import react.dom.render +import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import space.kscience.visionforge.Application @@ -29,7 +30,7 @@ private class MMDemoApp : Application { val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") - val context = Global.buildContext("demo") {} + val context = Context("demo") render(element) { child(MMApp) { attrs { diff --git a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt index 344c5c97..a9070af7 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt @@ -3,7 +3,6 @@ package space.kscience.visionforge.examples import space.kscience.dataforge.context.Context import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.gdml.toVision -import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.solid.Solids fun main() { @@ -11,7 +10,7 @@ fun main() { plugin(Solids) } - context.makeVisionFile( resourceLocation = ResourceLocation.EMBED) { + context.makeVisionFile { vision("canvas") { GdmlShowCase.babyIaxo().toVision() } } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 8c2170f6..630dc8dc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -46,5 +46,6 @@ include( ":demo:playground", ":demo:jupyter-playground", ":demo:plotly-fx", + ":demo:js-playground", ":jupyter:visionforge-gdml-jupyter" -) \ No newline at end of file +) diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt index f7de3070..4ce84033 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt @@ -24,6 +24,11 @@ public external interface MetaViewerProps : RProps { */ public var root: Meta + /** + * The title of root node + */ + public var rootName: String? + /** * Full path to the displayed node in [root]. Could be empty */ @@ -45,7 +50,7 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) { val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val actualItem = item ?: descriptorItem?.defaultValue - val token = props.name.lastOrNull()?.toString() ?: "Meta" + val token = props.name.lastOrNull()?.toString() ?: props.rootName ?: "" val expanderClick: (Event) -> Unit = { expanded = !expanded diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt index 5bb69714..7435f388 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt @@ -231,6 +231,7 @@ public val PropertyEditor: FunctionalComponent = functional this.name = Name.EMPTY this.descriptor = props.descriptor this.scope = props.scope + this.expanded = props.expanded } } } diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt index 6361ecfb..035ca3e7 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt @@ -1,5 +1,8 @@ package space.kscience.visionforge.react +import kotlinx.css.margin +import kotlinx.css.padding +import kotlinx.css.px import kotlinx.html.InputType import kotlinx.html.js.onChangeFunction import org.w3c.dom.HTMLInputElement @@ -8,36 +11,63 @@ import react.FunctionalComponent import react.dom.attrs import react.functionalComponent import react.useState +import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string import space.kscience.dataforge.values.asValue +import styled.css import styled.styledInput @JsExport public val RangeValueChooser: FunctionalComponent = functionalComponent("RangeValueChooser") { props -> - var innerValue by useState(props.item.string) + var innerValue by useState(props.item.double) + var rangeDisabled: Boolean by useState(props.item == null) + + val handleDisable: (Event) -> Unit = { + val checkBoxValue = (it.target as HTMLInputElement).checked + rangeDisabled = !checkBoxValue + if(!checkBoxValue) { + props.valueChanged?.invoke(null) + } else { + props.valueChanged?.invoke(innerValue?.asValue()) + } + } val handleChange: (Event) -> Unit = { val newValue = (it.target as HTMLInputElement).value props.valueChanged?.invoke(newValue.toDoubleOrNull()?.asValue()) - innerValue = newValue + innerValue = newValue.toDoubleOrNull() } - styledInput(type = InputType.range) { - attrs { - value = innerValue ?: "" - onChangeFunction = handleChange - val minValue = props.descriptor?.attributes?.get("min").string - minValue?.let { - min = it + flexRow { + styledInput(type = InputType.checkBox) { + css{ + padding(0.px) + margin(0.px) } - val maxValue = props.descriptor?.attributes?.get("max").string - maxValue?.let { - max = it + attrs { + defaultChecked = rangeDisabled.not() + onChangeFunction = handleDisable } - props.descriptor?.attributes?.get("step").string?.let { - step = it + } + + styledInput(type = InputType.range) { + attrs { + disabled = rangeDisabled + value = innerValue?.toString() ?: "" + onChangeFunction = handleChange + val minValue = props.descriptor?.attributes?.get("min").string + minValue?.let { + min = it + } + val maxValue = props.descriptor?.attributes?.get("max").string + maxValue?.let { + max = it + } + props.descriptor?.attributes?.get("step").string?.let { + step = it + } } } } 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 1b1d6ce6..c2e7e8d6 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 @@ -1,9 +1,8 @@ package space.kscience.visionforge.react -import kotlinx.css.Display -import kotlinx.css.display -import kotlinx.css.height -import kotlinx.css.pct +import kotlinx.css.FlexBasis +import kotlinx.css.flexBasis +import kotlinx.css.flexGrow import org.w3c.dom.Element import org.w3c.dom.HTMLElement import react.* @@ -19,8 +18,8 @@ import styled.styledDiv public external interface ThreeCanvasProps : RProps { public var context: Context - public var options: Canvas3DOptions - public var obj: Solid? + public var options: Canvas3DOptions? + public var solid: Solid? public var selected: Name? } @@ -37,15 +36,15 @@ public val ThreeCanvasComponent: FunctionalComponent = functio val three: ThreePlugin = useMemo({ props.context.fetch(ThreePlugin) }, arrayOf(props.context)) - useEffect(listOf(props.obj, props.options, elementRef)) { + useEffect(listOf(props.solid, props.options, elementRef)) { if (canvas == null) { val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") - canvas = three.getOrCreateCanvas(element, props.options) + canvas = three.getOrCreateCanvas(element, props.options ?: Canvas3DOptions()) } } - useEffect(listOf(canvas, props.obj)) { - props.obj?.let { obj -> + useEffect(listOf(canvas, props.solid)) { + props.solid?.let { obj -> canvas?.render(obj) } } @@ -56,8 +55,8 @@ public val ThreeCanvasComponent: FunctionalComponent = functio styledDiv { css { - display = Display.contents - height = 100.pct + flexGrow = 1.0 + flexBasis = FlexBasis.fill } ref = elementRef } diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt index 5cfd8651..7a32ef4f 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt @@ -21,6 +21,7 @@ import styled.styledSelect public external interface ValueChooserProps : RProps { public var item: MetaItem? public var descriptor: ValueDescriptor? + public var nullable: Boolean? public var valueChanged: ((Value?) -> Unit)? } diff --git a/ui/ring/src/main/kotlin/ringui/tabs/SmartTabs.kt b/ui/ring/src/main/kotlin/ringui/tabs/SmartTabs.kt index 574224ef..081181a3 100644 --- a/ui/ring/src/main/kotlin/ringui/tabs/SmartTabs.kt +++ b/ui/ring/src/main/kotlin/ringui/tabs/SmartTabs.kt @@ -4,14 +4,14 @@ import react.RBuilder import react.RHandler import react.dom.WithClassName -public external interface SmartTabsProps: WithClassName { +public external interface SmartTabsProps : WithClassName { public var initSelected: String } -public fun RBuilder.ringSmartTabs(active: String? = null, handler: RHandler){ - TabsModule.SmartTabs{ - active?.let{ +public fun RBuilder.ringSmartTabs(active: String? = null, handler: RHandler) { + TabsModule.SmartTabs { + active?.let { attrs { initSelected = active } diff --git a/ui/ring/src/main/kotlin/ringui/tabs/Tabs.kt b/ui/ring/src/main/kotlin/ringui/tabs/Tabs.kt index ec30223b..6c029110 100644 --- a/ui/ring/src/main/kotlin/ringui/tabs/Tabs.kt +++ b/ui/ring/src/main/kotlin/ringui/tabs/Tabs.kt @@ -40,10 +40,11 @@ public fun RBuilder.ringTabs(active: String? = null, handler: RHandler) { +public fun RBuilder.ringTab(title: dynamic, id: String = title.toString(), handler: RHandler) { TabsModule.Tab { attrs { this.title = title + this.id = id } handler() } diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index 10d5fdfb..5336de06 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -7,21 +7,20 @@ import ringui.grid.ringGrid import ringui.grid.ringRow import space.kscience.dataforge.context.Context import space.kscience.dataforge.names.Name -import space.kscience.visionforge.Vision import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.specifications.Canvas3DOptions import styled.css import styled.styledDiv -public external interface ThreeViewWithControlsProps : RProps { +public external interface ThreeCanvasWithControlsProps : RProps { public var context: Context - public var vision: Vision? + public var solid: Solid? public var selected: Name? } @JsExport -public val ThreeViewWithControls: (props: ThreeViewWithControlsProps) -> dynamic = +public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dynamic = functionalComponent("ThreeViewWithControls") { props -> var selected by useState { props.selected } val onSelect: (Name?) -> Unit = { @@ -51,7 +50,7 @@ public val ThreeViewWithControls: (props: ThreeViewWithControlsProps) -> dynamic child(ThreeCanvasComponent) { attrs { this.context = props.context - this.obj = props.vision as? Solid + this.solid = props.solid as? Solid this.selected = selected this.options = options } @@ -72,7 +71,7 @@ public val ThreeViewWithControls: (props: ThreeViewWithControlsProps) -> dynamic height = 100.pct overflowY = Overflow.auto } - ringThreeControls(options, props.vision, selected, onSelect) + ringThreeControls(options, props.solid, selected, onSelect) } } } diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt index 6af8450c..52586fc5 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt @@ -25,10 +25,10 @@ public class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { react.dom.render(element) { - child(ThreeViewWithControls) { + child(ThreeCanvasWithControls) { attrs { this.context = this@ThreeWithControls.context - this.vision = vision + this.solid = vision as? Solid } } } diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt index 680bc6bf..ed082f03 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt @@ -2,7 +2,9 @@ package space.kscience.visionforge.ring import org.w3c.dom.Element import react.RBuilder +import react.dom.p import react.dom.render +import react.useMemo import ringui.island.ringIsland import ringui.island.ringIslandContent import ringui.island.ringIslandHeader @@ -10,6 +12,7 @@ import ringui.tabs.ringSmartTabs import ringui.tabs.ringTab import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.visionforge.* +import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference @@ -20,37 +23,55 @@ public fun RBuilder.ringPropertyEditor( key: Any? = null, ) { - ringIsland("Properties") { - propertyEditor( - ownProperties = vision.ownProperties, - allProperties = vision.allProperties(), - updateFlow = vision.propertyChanges, - descriptor = descriptor, - key = key - ) + val styles = useMemo(vision, key) { + if (vision is SolidReference) { + (vision.styles + vision.prototype.styles).distinct() + } else { + vision.styles + } } - val styles = if (vision is SolidReference) { - (vision.styles + vision.prototype.styles).distinct() - } else { - vision.styles - } - if (styles.isNotEmpty()) { - ringIsland { - ringIslandHeader { - attrs { - border = true + flexColumn { + ringIsland("Properties") { + propertyEditor( + ownProperties = vision.ownProperties, + allProperties = vision.allProperties(), + updateFlow = vision.propertyChanges, + descriptor = descriptor, + key = key + ) + } + + if (styles.isNotEmpty()) { + ringIsland { + ringIslandHeader { + attrs { + border = true + } + +"Styles" } - +"Styles" - } - ringIslandContent { - ringSmartTabs { - styles.forEach { styleName -> + ringIslandContent { + if (styles.size == 1) { + val styleName = styles.first() + p{ + +styleName + } val style = vision.getStyle(styleName) if (style != null) { - ringTab(styleName) { + ringTab(styleName, id = styleName) { metaViewer(style) } } + } else { + ringSmartTabs { + styles.forEach { styleName -> + val style = vision.getStyle(styleName) + if (style != null) { + ringTab(styleName, id = styleName) { + metaViewer(style) + } + } + } + } } } } diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt index c88838d6..38ed513d 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt @@ -92,22 +92,25 @@ public external interface ThreeControlsProps : RProps { @JsExport public val ThreeControls: FunctionalComponent = functionalComponent { props -> - ringSmartTabs(if (props.selected != null) "Properties" else null) { + ringSmartTabs(if (props.selected != null) "Properties" else "Tree") { ringTab("Canvas") { ringIsland("Canvas configuration") { canvasControls(props.canvasOptions, props.vision) } } ringTab("Tree") { - styledDiv { + flexColumn { css { border(1.px, BorderStyle.solid, Color.lightGray) padding(10.px) + flexGrow = 1.0 + flexWrap = FlexWrap.wrap } h2 { +"Object tree" } styledDiv { css { - flex(1.0, 1.0, FlexBasis.inherit) + overflowY = Overflow.auto + flexGrow = 1.0 } props.vision?.let { objectTree(it, props.selected, props.onSelect) @@ -115,15 +118,17 @@ public val ThreeControls: FunctionalComponent = functionalCo } } } - ringTab("Properties") { - props.selected.let { selected -> - val selectedObject: Vision? = when { - selected == null -> null - selected.isEmpty() -> props.vision - else -> (props.vision as? VisionGroup)?.get(selected) - } - if (selectedObject != null) { - ringPropertyEditor(selectedObject, key = selected) + if (props.selected != null) { + ringTab("Properties") { + props.selected.let { selected -> + val selectedObject: Vision? = when { + selected == null -> null + selected.isEmpty() -> props.vision + else -> (props.vision as? VisionGroup)?.get(selected) + } + if (selectedObject != null) { + ringPropertyEditor(selectedObject, key = selected) + } } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 066de405..bab671d9 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -11,7 +11,7 @@ import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.* import space.kscience.visionforge.* -public interface SolidReference : Vision { +public interface SolidReference : Solid { public val prototype: Solid } @@ -76,11 +76,7 @@ public class SolidReferenceGroup( return if (name.isEmpty()) prototype else { val proto = (prototype as? SolidGroup)?.get(name) ?: error("Prototype with name $name not found in SolidReferenceGroup $refName") - when (proto) { - is Solid -> proto - is SolidReference -> proto.prototype - else -> error("Prototype with name $name is ${proto::class} but expected Solid") - } + proto as? Solid ?: error("Prototype with name $name is ${proto::class} but expected Solid") } } @@ -100,6 +96,22 @@ public class SolidReferenceGroup( */ private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup { + //TODO replace by properties + override var position: Point3D? + get() = prototype.position + set(value) { + error("Can't set position of reference") + } + override var rotation: Point3D? + get() = prototype.rotation + set(value) { + error("Can't set position of reference") + } + override var scale: Point3D? + get() = prototype.scale + set(value) { + error("Can't set position of reference") + } override val prototype: Solid get() = prototypeFor(childName) override val children: Map 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 0d937565..525bf402 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 @@ -103,27 +103,31 @@ public class ThreeCanvas( }.apply { setClearColor(Colors.skyblue, 1) //Clipping planes - localClippingEnabled = true options.onChange(this@ThreeCanvas) { name, _, _ -> if (name.startsWith(Canvas3DOptions::clipping.name.asName())) { + localClippingEnabled = true val clipping = options.clipping - boundingBox?.let { boundingBox -> - val xClippingPlane = clipping.x?.let { - val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it - Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue) + if(!clipping.isEmpty()) { + boundingBox?.let { boundingBox -> + val xClippingPlane = clipping.x?.let { + val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it + Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue) - } - val yClippingPlane = clipping.y?.let { - val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it - Plane(Vector3(0.0, -1.0, 0.0), absoluteValue) - } + } + val yClippingPlane = clipping.y?.let { + val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it + Plane(Vector3(0.0, -1.0, 0.0), absoluteValue) + } - val zClippingPlane = clipping.z?.let { - val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it - Plane(Vector3(0.0, 0.0, -1.0), absoluteValue) + val zClippingPlane = clipping.z?.let { + val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it + Plane(Vector3(0.0, 0.0, -1.0), absoluteValue) + } + clippingPlanes = listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray() } - clippingPlanes = listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray() } + } else { + localClippingEnabled = false } } }